launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #32422
[Merge] ~alvarocs/launchpad:use-craft-platforms-for-snap into launchpad:master
Alvaro Crespo Serrano has proposed merging ~alvarocs/launchpad:use-craft-platforms-for-snap into launchpad:master.
Commit message:
Support craft-platforms build plan in charm builds
Updates Launchpad to support charm builds using 'craft-platforms' for the unified format. It also ensures the platform name ('craft_platform') is passed through the build pipeline.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~alvarocs/launchpad/+git/launchpad/+merge/485084
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~alvarocs/launchpad:use-craft-platforms-for-snap into launchpad:master.
diff --git a/lib/lp/snappy/adapters/buildarch.py b/lib/lp/snappy/adapters/buildarch.py
index 55e9238..31b77d8 100644
--- a/lib/lp/snappy/adapters/buildarch.py
+++ b/lib/lp/snappy/adapters/buildarch.py
@@ -6,6 +6,8 @@ __all__ = ["determine_architectures_to_build", "BadPropertyError"]
from collections import Counter
from typing import Any, Dict, List, Optional, Union
+from craft_platforms import BuildInfo, CraftPlatformsError, get_build_plan
+
from lp.services.helpers import english_list
from lp.snappy.interfaces.snapbase import SnapBaseFeature
from lp.snappy.model.snapbase import SnapBase
@@ -83,6 +85,16 @@ class UnsupportedBuildOnError(SnapArchitecturesParserError):
self.build_on = build_on
+class CraftPlatformsBuildPlanError(SnapArchitecturesParserError):
+ """Error raised when craft-platforms fails while generating
+ a build plan."""
+
+ def __init__(Self, message, resolution=None):
+ if resolution:
+ message += f" Resolution: {resolution}"
+ super().__init__(message)
+
+
class SnapArchitecture:
"""A single entry in the snapcraft.yaml 'architectures' list."""
@@ -91,6 +103,7 @@ class SnapArchitecture:
build_on: Union[str, List[str]],
build_for: Optional[Union[str, List[str]]] = None,
build_error: Optional[str] = None,
+ build_info: Optional[BuildInfo] = None,
):
"""Create a new architecture entry.
@@ -111,6 +124,7 @@ class SnapArchitecture:
else:
self.build_for = self.build_on
self.build_error = build_error
+ self.build_info = build_info
@classmethod
def from_dict(cls, properties):
@@ -132,7 +146,7 @@ class SnapArchitecture:
class SnapBuildInstance:
"""A single instance of a snap that should be built.
- It has two useful attributes:
+ If has the following useful attributes:
- architecture: The architecture tag that should be used to build the
snap.
@@ -141,18 +155,21 @@ class SnapBuildInstance:
in the case of cross-building)
- required: Whether or not failure to build should cause the entire
set to fail.
+ - platform_name: The platform to build for.
"""
def __init__(
self,
architecture: SnapArchitecture,
supported_architectures: List[str],
+ platform_name: str = None,
):
"""Construct a new `SnapBuildInstance`.
:param architecture: `SnapArchitecture` instance.
:param supported_architectures: List of supported architectures,
sorted by priority.
+ : param platform_name: The platform to build for.
"""
build_on = architecture.build_on
# "all" indicates that the architecture doesn't matter. Try to pick
@@ -172,6 +189,7 @@ class SnapBuildInstance:
self.target_architectures = architecture.build_for
self.required = architecture.build_error != "ignore"
+ self.platform_name = platform_name
def determine_architectures_to_build(
@@ -187,16 +205,24 @@ def determine_architectures_to_build(
we can create builds for.
:return: a list of `SnapBuildInstance`s.
"""
- architectures_list: Optional[List] = snapcraft_data.get("architectures")
-
- if architectures_list:
- architectures = parse_architectures_list(architectures_list)
- elif "platforms" in snapcraft_data:
- snap_base_name = snap_base.name if snap_base else "unknown"
- architectures = parse_platforms(
- snapcraft_data, supported_arches, snap_base_name
+ architectures = None
+ # 1) Snap with 'architectures' format
+ if "architectures" in snapcraft_data and snapcraft_data.get(
+ "architectures"
+ ):
+ # XXX tushar5526 2025-04-15: craft_platforms do not support
+ # "architectures" format used in core22 or older,
+ # fallback to the existing LP parsing logic in that case.
+ architectures_list: Optional[List] = snapcraft_data.get(
+ "architectures"
)
- else:
+ architectures = parse_architectures_list(architectures_list)
+ # 2) Snap with 'platforms' format
+ elif "platforms" in snapcraft_data and snapcraft_data.get("platforms"):
+ # Use craft-platforms to generate the build plan
+ architectures = parse_platforms(snapcraft_data)
+ # 3) Snap with no 'architectures' or 'platforms' format
+ if not architectures:
# If no architectures are specified, build one for each supported
# architecture.
architectures = [
@@ -235,40 +261,55 @@ def parse_architectures_list(
def parse_platforms(
snapcraft_data: Dict[str, Any],
- supported_arches: List[str],
- base_name: str,
+ # supported_arches: List[str],
+ # base_name: str,
) -> List[SnapArchitecture]:
- architectures = []
- supported_arch_names = supported_arches
-
- for platform, configuration in snapcraft_data["platforms"].items():
- # The 'platforms' property and its values look like
- # platforms:
- # ubuntu-amd64:
- # build-on: [amd64]
- # build-for: [amd64]
- # 'ubuntu-amd64' will be the value of 'platform' and its value dict
- # containing the keys 'build-on', 'build-for' will be the value of
- # 'configuration'.
- if configuration:
- build_on = configuration.get("build-on", [platform])
- build_for = configuration.get("build-for", build_on)
- architectures.append(
- SnapArchitecture(
- build_on=build_on,
- build_for=build_for,
- )
- )
- elif platform in supported_arch_names:
- architectures.append(
- SnapArchitecture(build_on=[platform], build_for=[platform])
- )
- else:
- raise BadPropertyError(
- f"'{platform}' is not a supported platform for '{base_name}'."
- )
- return architectures
+ try:
+ exhaustive_build_plan = get_build_plan(
+ app="snapcraft",
+ project_data=snapcraft_data,
+ )
+ # XXX alvarocs 2025-04-04: craft-platforms currently raises
+ # 'ValueError' when it encounters malformed input such as an invalid
+ # base or platform name. These should instead raise
+ # 'CraftPlatformsError'. Bug tracked at:
+ # https://github.com/canonical/craft-platforms/issues/116
+ except (CraftPlatformsError, ValueError) as e:
+ message = getattr(e, "message", str(e))
+ resolution = getattr(e, "resolution", None)
+ raise CraftPlatformsBuildPlanError(
+ "Failed to compute the build plan for the snapcraft file "
+ "with error: "
+ f"{message}",
+ resolution=resolution,
+ )
+ platform_plans: Dict[str, BuildInfo] = {}
+ for plan in exhaustive_build_plan:
+ platform_plans.setdefault(plan.platform, []).append(plan)
+ instances_to_build: List[BuildInfo] = []
+ for _platform, pairs in platform_plans.items():
+ # One way of building for that platform, i.e one (info, das)
+ if len(pairs) == 1:
+ instances_to_build.append(pairs[0])
+ continue
+ # Multiple ways of building for that platform:
+ for info in pairs:
+ # Pick the native build
+ if info.build_on == info.build_for:
+ instances_to_build.append(info)
+ break
+ # Pick first one if none are native
+ else:
+ instances_to_build.append(pairs[0])
+ return [
+ SnapArchitecture(
+ build_on=str(instance.build_on),
+ build_for=str(instance.build_for),
+ build_info=instance,
+ )
+ for instance in instances_to_build
+ ]
def validate_architectures(architectures: List[SnapArchitecture]):
@@ -298,8 +339,13 @@ def build_architectures_list(
architectures_to_build = []
for arch in architectures:
try:
+ platform_name = (
+ arch.build_info.platform
+ if arch.build_info is not None
+ else None
+ )
architectures_to_build.append(
- SnapBuildInstance(arch, supported_arches)
+ SnapBuildInstance(arch, supported_arches, platform_name)
)
except UnsupportedBuildOnError:
# Snaps are allowed to declare that they build on architectures
diff --git a/lib/lp/snappy/adapters/tests/test_buildarch.py b/lib/lp/snappy/adapters/tests/test_buildarch.py
index be9528c..bac0128 100644
--- a/lib/lp/snappy/adapters/tests/test_buildarch.py
+++ b/lib/lp/snappy/adapters/tests/test_buildarch.py
@@ -9,7 +9,7 @@ from testtools.matchers import HasLength, MatchesException, Raises
from lp.snappy.adapters.buildarch import (
AllConflictInBuildForError,
AllConflictInBuildOnError,
- BadPropertyError,
+ CraftPlatformsBuildPlanError,
DuplicateBuildOnError,
SnapArchitecture,
SnapBuildInstance,
@@ -209,7 +209,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
scenarios = [
(
- "none",
+ "none architectures, build one per supported architecture",
{
"architectures": None,
"supported_architectures": ["amd64", "i386", "armhf"],
@@ -218,16 +218,19 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": None,
},
{
"architecture": "i386",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": None,
},
{
"architecture": "armhf",
"target_architectures": ["armhf"],
"required": True,
+ "platform_name": None,
},
],
},
@@ -244,6 +247,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "i386",
"target_architectures": ["amd64", "i386"],
"required": True,
+ "platform_name": None,
}
],
},
@@ -258,6 +262,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["all"],
"required": True,
+ "platform_name": None,
}
],
},
@@ -275,11 +280,13 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": None,
},
{
"architecture": "i386",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": None,
},
],
},
@@ -297,11 +304,13 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": None,
},
{
"architecture": "i386",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": None,
},
],
},
@@ -324,16 +333,19 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": None,
},
{
"architecture": "i386",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": None,
},
{
"architecture": "armhf",
"target_architectures": ["armhf"],
"required": False,
+ "platform_name": None,
},
],
},
@@ -350,6 +362,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["all"],
"required": True,
+ "platform_name": None,
}
],
},
@@ -366,6 +379,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "i386",
"target_architectures": ["all"],
"required": True,
+ "platform_name": None,
}
],
},
@@ -380,6 +394,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "i386",
"target_architectures": ["amd64", "i386"],
"required": True,
+ "platform_name": None,
}
],
},
@@ -394,6 +409,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64", "i386"],
"required": True,
+ "platform_name": None,
}
],
},
@@ -412,11 +428,13 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": None,
},
{
"architecture": "i386",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": None,
},
],
},
@@ -459,11 +477,13 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": None,
},
{
"architecture": "amd64",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": None,
},
],
},
@@ -485,6 +505,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
(
"platforms with configuration",
{
+ "base": "core24",
"platforms": {
"ubuntu-amd64": {
"build-on": ["amd64"],
@@ -501,11 +522,13 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": "ubuntu-amd64",
},
{
"architecture": "i386",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": "ubuntu-i386",
},
],
},
@@ -513,9 +536,10 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
(
"platforms with shorthand configuration",
{
+ "base": "core24",
"platforms": {
- "amd64": {},
- "i386": {},
+ "amd64": None,
+ "i386": None,
},
"supported_architectures": ["amd64", "i386", "armhf"],
"expected": [
@@ -523,11 +547,13 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": "amd64",
},
{
"architecture": "i386",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": "i386",
},
],
},
@@ -535,20 +561,27 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
(
"platforms with unsupported architecture",
{
+ "base": "core24",
"platforms": {
- "ubuntu-unsupported": {},
+ "ubuntu-unsupported": None,
},
"supported_architectures": ["amd64", "i386", "armhf"],
"expected_exception": MatchesException(
- BadPropertyError,
- r"\'ubuntu-unsupported\' is not a supported platform for "
- r"\'snap-base-name-.*\'",
+ CraftPlatformsBuildPlanError,
+ "Failed to compute the build plan for the snapcraft "
+ r"file with error*",
),
},
),
(
+ # multiple architecture values in "build-for" and "build-on"
+ # is not allowed by snapcraft and such configs are invalid.
+ # As craft_platforms is a separate, generalized API, it still
+ # returns a build plan which we then filter and pair a native
+ # build with native architecture to run on.
"platforms with multiple architectures",
{
+ "base": "core24",
"platforms": {
"ubuntu-amd64-i386": {
"build-on": ["amd64", "i386"],
@@ -559,8 +592,9 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"expected": [
{
"architecture": "amd64",
- "target_architectures": ["amd64", "i386"],
+ "target_architectures": ["amd64"],
"required": True,
+ "platform_name": "ubuntu-amd64-i386",
},
],
},
@@ -568,6 +602,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
(
"platforms with conflict in build-on",
{
+ "base": "core24",
"platforms": {
"ubuntu-conflict": {
"build-on": ["all", "amd64"],
@@ -575,13 +610,16 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
},
"supported_architectures": ["amd64", "i386", "armhf"],
"expected_exception": MatchesException(
- AllConflictInBuildOnError
+ CraftPlatformsBuildPlanError,
+ "Failed to compute the build plan for the snapcraft "
+ "file with error: 'all' is not a valid DebianArchitecture",
),
},
),
(
"platforms with conflict in build-for",
{
+ "base": "core24",
"platforms": {
"ubuntu-conflict": {
"build-on": ["amd64"],
@@ -590,41 +628,55 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
},
"supported_architectures": ["amd64", "i386", "armhf"],
"expected_exception": MatchesException(
- AllConflictInBuildForError, r".*"
+ CraftPlatformsBuildPlanError,
+ "Failed to compute the build plan for the snapcraft "
+ "file with error: build-for: all must be the only "
+ "build-for architecture Resolution: Provide only one "
+ "platform with only build-for: all or remove 'all' from "
+ "build-for options.",
),
},
),
(
- "platforms with unsupported architecture in build-on",
+ "platforms with invalid architecture in build-on",
{
+ "base": "core24",
"platforms": {
"ubuntu-amd64": {
- "build-on": ["unsupported"],
+ "build-on": ["invalid"],
"build-for": ["amd64"],
},
},
"supported_architectures": ["amd64", "i386", "armhf"],
- # Launchpad ignores architectures that it does not know about
- "expected": [],
+ "expected_exception": MatchesException(
+ CraftPlatformsBuildPlanError,
+ (
+ "Failed to compute the build plan for the snapcraft "
+ "file with error: 'invalid' is not a valid "
+ "DebianArchitecture"
+ ),
+ ),
},
),
(
- "platforms with 1/2 unsupported architectures in build-on",
+ "platforms with invalid architecture in build-for",
{
+ "base": "core24",
"platforms": {
"ubuntu-amd64": {
- "build-on": ["unsupported", "amd64"],
- "build-for": ["amd64"],
+ "build-on": ["amd64"],
+ "build-for": ["invalid"],
},
},
"supported_architectures": ["amd64", "i386", "armhf"],
- "expected": [
- {
- "architecture": "amd64",
- "target_architectures": ["amd64"],
- "required": True,
- },
- ],
+ "expected_exception": MatchesException(
+ CraftPlatformsBuildPlanError,
+ (
+ "Failed to compute the build plan for the snapcraft "
+ "file with error: 'invalid' is not a valid "
+ "DebianArchitecture"
+ ),
+ ),
},
),
(
@@ -633,6 +685,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"snap_base_features": {
SnapBaseFeature.ALLOW_DUPLICATE_BUILD_ON: False
},
+ "base": "core24",
"platforms": {
"ubuntu-amd64": {
"build-on": ["amd64"],
@@ -653,6 +706,7 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"snap_base_features": {
SnapBaseFeature.ALLOW_DUPLICATE_BUILD_ON: True
},
+ "base": "core24",
"platforms": {
"ubuntu-amd64": {
"build-on": ["amd64"],
@@ -669,18 +723,21 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
"architecture": "amd64",
"target_architectures": ["amd64"],
"required": True,
+ "platform_name": "ubuntu-amd64",
},
{
"architecture": "amd64",
"target_architectures": ["i386"],
"required": True,
+ "platform_name": "ubuntu-amd64-i386",
},
],
},
),
(
- "platforms with all keyword",
+ "platforms with 'all' keyword in 'build-on'",
{
+ "base": "core24",
"platforms": {
"ubuntu-all": {
"build-on": ["all"],
@@ -688,11 +745,52 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
},
},
"supported_architectures": ["amd64", "i386", "armhf"],
+ "expected_exception": MatchesException(
+ CraftPlatformsBuildPlanError,
+ "Failed to compute the build plan for the snapcraft "
+ "file with error: 'all' is not a valid DebianArchitecture",
+ ),
+ },
+ ),
+ (
+ "platforms with 'all' keyword in 'build-for'",
+ {
+ "base": "core24",
+ "platforms": {
+ "ubuntu-all": {
+ "build-on": ["amd64"],
+ "build-for": ["all"],
+ },
+ },
+ "supported_architectures": ["amd64", "i386", "armhf"],
"expected": [
{
"architecture": "amd64",
"target_architectures": ["all"],
"required": True,
+ "platform_name": "ubuntu-all",
+ },
+ ],
+ },
+ ),
+ (
+ "empty platforms dictionary",
+ {
+ "base": "core24",
+ "platforms": {},
+ "supported_architectures": ["amd64", "i386"],
+ "expected": [
+ {
+ "architecture": "amd64",
+ "target_architectures": ["amd64"],
+ "required": True,
+ "platform_name": None,
+ },
+ {
+ "architecture": "i386",
+ "target_architectures": ["i386"],
+ "required": True,
+ "platform_name": None,
},
],
},
@@ -705,6 +803,12 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCaseWithFactory):
snapcraft_data["architectures"] = self.architectures
if hasattr(self, "platforms"):
snapcraft_data["platforms"] = self.platforms
+ if hasattr(self, "base"):
+ snapcraft_data["base"] = self.base
+ # if hasattr(self, "type"):
+ # snapcraft_data["type"] = self.type
+ # if hasattr(self, "build-base"):
+ # snapcraft_data["build-base"] = self.build_base
snap_base_features = getattr(self, "snap_base_features", {})
snap_base = self.factory.makeSnapBase(features=snap_base_features)
if hasattr(self, "expected_exception"):
diff --git a/lib/lp/snappy/interfaces/snap.py b/lib/lp/snappy/interfaces/snap.py
index 90bfbed..890dcbc 100644
--- a/lib/lp/snappy/interfaces/snap.py
+++ b/lib/lp/snappy/interfaces/snap.py
@@ -471,6 +471,7 @@ class ISnapView(Interface):
channels=None,
build_request=None,
target_architectures=None,
+ craft_platfrom=None,
):
"""Request that the snap package be built.
@@ -485,6 +486,7 @@ class ISnapView(Interface):
if any.
:param target_architectures: The optional list of target architectures
to build the snap for.
+ :param craft_platform: The platform name to build for.
:return: `ISnapBuild`.
"""
diff --git a/lib/lp/snappy/interfaces/snapbuild.py b/lib/lp/snappy/interfaces/snapbuild.py
index 3416200..ef877a9 100644
--- a/lib/lp/snappy/interfaces/snapbuild.py
+++ b/lib/lp/snappy/interfaces/snapbuild.py
@@ -352,6 +352,14 @@ class ISnapBuildView(IPackageBuildView, IPrivacy):
)
)
+ craft_platform = exported(
+ TextLine(
+ title=_("Craft platform name"),
+ required=False,
+ readonly=True,
+ )
+ )
+
def getFiles():
"""Retrieve the build's `ISnapFile` records.
@@ -432,6 +440,7 @@ class ISnapBuildSet(ISpecificBuildFarmJobSource):
store_upload_metadata=None,
build_request=None,
target_architectures=None,
+ craft_platform=None,
):
"""Create an `ISnapBuild`."""
diff --git a/lib/lp/snappy/model/snap.py b/lib/lp/snappy/model/snap.py
index 735dd1d..dfe2282 100644
--- a/lib/lp/snappy/model/snap.py
+++ b/lib/lp/snappy/model/snap.py
@@ -858,6 +858,7 @@ class Snap(StormBase, WebhookTargetMixin):
channels=None,
build_request=None,
target_architectures: t.Optional[t.List[str]] = None,
+ craft_platform: str = None,
) -> ISnapBuild:
"""See `ISnap`."""
self._checkRequestBuild(requester, archive)
@@ -884,6 +885,7 @@ class Snap(StormBase, WebhookTargetMixin):
SnapBuild.target_architectures == target_architectures,
channels_clause,
SnapBuild.status == BuildStatus.NEEDSBUILD,
+ SnapBuild.craft_platform == craft_platform,
)
if pending.any() is not None:
raise SnapBuildAlreadyPending
@@ -898,6 +900,7 @@ class Snap(StormBase, WebhookTargetMixin):
channels=channels,
build_request=build_request,
target_architectures=target_architectures,
+ craft_platform=craft_platform,
)
build.queueBuild()
notify(ObjectCreatedEvent(build, user=requester))
@@ -1059,13 +1062,15 @@ class Snap(StormBase, WebhookTargetMixin):
channels=arch_channels,
build_request=build_request,
target_architectures=build_instance.target_architectures,
+ craft_platform=build_instance.platform_name,
)
if logger is not None:
logger.debug(
- " - %s/%s/%s: Build requested.",
+ " - %s/%s/%s/%s: Build requested.",
self.owner.name,
self.name,
arch,
+ build_instance.platform_name,
)
builds.append(build)
except SnapBuildAlreadyPending:
@@ -1075,7 +1080,12 @@ class Snap(StormBase, WebhookTargetMixin):
raise
elif logger is not None:
logger.exception(
- " - %s/%s/%s: %s", self.owner.name, self.name, arch, e
+ " - %s/%s/%s/%s: %s",
+ self.owner.name,
+ self.name,
+ arch,
+ build_instance.platform_name,
+ e,
)
return builds
diff --git a/lib/lp/snappy/model/snapbuild.py b/lib/lp/snappy/model/snapbuild.py
index 842b142..5d83c3e 100644
--- a/lib/lp/snappy/model/snapbuild.py
+++ b/lib/lp/snappy/model/snapbuild.py
@@ -186,6 +186,8 @@ class SnapBuild(PackageBuildMixin, StormBase):
store_upload_metadata = JSON("store_upload_json_data", allow_none=True)
+ craft_platform = Unicode(name="craft_platform", allow_none=True)
+
def __init__(
self,
build_farm_job,
@@ -202,6 +204,7 @@ class SnapBuild(PackageBuildMixin, StormBase):
store_upload_metadata=None,
build_request=None,
target_architectures=None,
+ craft_platform=None,
):
"""Construct a `SnapBuild`."""
super().__init__()
@@ -221,6 +224,7 @@ class SnapBuild(PackageBuildMixin, StormBase):
if build_request is not None:
self.build_request_id = build_request.id
self.status = BuildStatus.NEEDSBUILD
+ self.craft_platform = craft_platform
@property
def build_request(self):
@@ -566,6 +570,7 @@ class SnapBuildSet(SpecificBuildFarmJobSourceMixin):
store_upload_metadata=None,
build_request=None,
target_architectures=None,
+ craft_platform=None,
):
"""See `ISnapBuildSet`."""
store = IPrimaryStore(SnapBuild)
@@ -593,6 +598,7 @@ class SnapBuildSet(SpecificBuildFarmJobSourceMixin):
store_upload_metadata=store_upload_metadata,
build_request=build_request,
target_architectures=target_architectures,
+ craft_platform=craft_platform,
)
store.add(snapbuild)
store.flush()
diff --git a/lib/lp/snappy/model/snapbuildbehaviour.py b/lib/lp/snappy/model/snapbuildbehaviour.py
index bfbb1e5..cbd9391 100644
--- a/lib/lp/snappy/model/snapbuildbehaviour.py
+++ b/lib/lp/snappy/model/snapbuildbehaviour.py
@@ -201,6 +201,8 @@ class SnapBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
args["target_architectures"] = removeSecurityProxy(
build.target_architectures
)
+ if build.craft_platform:
+ args["craft_platform"] = build.craft_platform
return args
def verifySuccessfulBuild(self):
diff --git a/lib/lp/snappy/tests/test_snap.py b/lib/lp/snappy/tests/test_snap.py
index df0343b..dba8e01 100644
--- a/lib/lp/snappy/tests/test_snap.py
+++ b/lib/lp/snappy/tests/test_snap.py
@@ -696,6 +696,23 @@ class TestSnap(TestCaseWithFactory):
),
)
+ def test_requestBuild_with_platform_name(self):
+ processor = self.factory.makeProcessor(supports_virtualized=True)
+ distroarchseries = self.makeBuildableDistroArchSeries(
+ processor=processor
+ )
+ snap = self.factory.makeSnap(
+ distroseries=distroarchseries.distroseries, processors=[processor]
+ )
+ build = snap.requestBuild(
+ snap.owner,
+ snap.distro_series.main_archive,
+ distroarchseries,
+ PackagePublishingPocket.UPDATES,
+ craft_platform="ubuntu-amd64",
+ )
+ self.assertEqual("ubuntu-amd64", build.craft_platform)
+
def test_requestBuilds(self):
# requestBuilds schedules a job and returns a corresponding
# SnapBuildRequest.
Follow ups