launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #31499
[Merge] ~jugmac00/launchpad:parse-actual-rockcraft-yaml-format into launchpad:master
Jürgen Gmach has proposed merging ~jugmac00/launchpad:parse-actual-rockcraft-yaml-format into launchpad:master with ~jugmac00/launchpad:add-explicit-model-for-rock-bases as a prerequisite.
Commit message:
Parse actual rockcraft.yaml syntax
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jugmac00/launchpad/+git/launchpad/+merge/473458
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~jugmac00/launchpad:parse-actual-rockcraft-yaml-format into launchpad:master.
diff --git a/lib/lp/rocks/adapters/buildarch.py b/lib/lp/rocks/adapters/buildarch.py
index 281924a..bea1040 100644
--- a/lib/lp/rocks/adapters/buildarch.py
+++ b/lib/lp/rocks/adapters/buildarch.py
@@ -6,6 +6,10 @@ __all__ = [
]
import json
+<<<<<<< lib/lp/rocks/adapters/buildarch.py
+=======
+import re
+>>>>>>> lib/lp/rocks/adapters/buildarch.py
from collections import Counter, OrderedDict
from lp.services.helpers import english_list
@@ -18,10 +22,17 @@ class RockBasesParserError(Exception):
class MissingPropertyError(RockBasesParserError):
"""Error for when an expected property is not present in the YAML."""
+<<<<<<< lib/lp/rocks/adapters/buildarch.py
def __init__(self, prop):
super().__init__(
f"Base specification is missing the {prop!r} property"
)
+=======
+ def __init__(self, prop, msg=None):
+ if msg is None:
+ msg = f"Base specification is missing the {prop!r} property"
+ super().__init__(msg)
+>>>>>>> lib/lp/rocks/adapters/buildarch.py
self.property = prop
@@ -127,6 +138,86 @@ def determine_instances_to_build(
rockcraft_data, supported_arches, default_distro_series
):
=======
+class UnifiedRockBaseConfiguration:
+ """A unified base configuration in rockcraft.yaml"""
+
+ def __init__(self, build_on, run_on=None):
+ self.build_on = build_on
+ self.run_on = list(build_on) if run_on is None else run_on
+
+ @classmethod
+ def from_dict(cls, rockcraft_data, supported_arches):
+ base = rockcraft_data["base"]
+ if isinstance(base, str):
+ # Expected short-form value looks like 'ubuntu@24.04'
+ match = re.match(r"(.+)@(.+)", base)
+ if not match:
+ raise BadPropertyError(
+ f"Invalid value for base '{base}'. Expected value should "
+ "be like 'ubuntu@24.04'"
+ )
+ base_name, base_channel = match.groups()
+ else:
+ # Expected value looks like {"name": "ubuntu", "channel": "24.04"}
+ base_name = base["name"]
+ # If a value like 24.04 is unquoted in yaml, it will be
+ # interpreted as a float. So we convert it to a string.
+ base_channel = str(base["channel"])
+
+ # XXX jugmac00 2024-09-18: Find out if we need 'build-base' or not.
+ # There is no existing code that is using that.
+
+ platforms = rockcraft_data.get("platforms")
+ if not platforms:
+ raise MissingPropertyError(
+ "platforms", "The 'platforms' property is required"
+ )
+ configs = []
+ for platform, configuration in 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'.
+ name = base_name
+ channel = base_channel
+ if configuration:
+ build_on = configuration["build-on"]
+ if isinstance(build_on, str):
+ build_on = [build_on]
+
+ build_on = [
+ RockBase(name, channel, architecture)
+ for architecture in build_on
+ ]
+
+ build_for = configuration["build-for"]
+ if isinstance(build_for, str):
+ build_for = [build_for]
+
+ build_for = [
+ RockBase(name, channel, architecture)
+ for architecture in build_for
+ ]
+ else:
+ supported_arch_names = (
+ das.architecturetag for das in supported_arches
+ )
+ if platform in supported_arch_names:
+ build_on = [RockBase(name, channel, platform)]
+ build_for = [RockBase(name, channel, platform)]
+ else:
+ raise BadPropertyError(
+ f"'{platform}' is not a supported architecture "
+ f"for '{base_name}@{base_channel}'."
+ )
+ configs.append(cls(build_on, build_for))
+ return configs
+
+
def determine_instances_to_build(rockcraft_data, supported_arches):
>>>>>>> lib/lp/rocks/adapters/buildarch.py
"""Return a list of instances to build based on rockcraft.yaml.
@@ -166,8 +257,9 @@ def determine_instances_to_build(rockcraft_data, supported_arches):
=======
:return: A list of `DistroArchSeries`.
"""
- bases_list = rockcraft_data.get("bases")
- configs = [RockBaseConfiguration.from_dict(item) for item in bases_list]
+ configs = UnifiedRockBaseConfiguration.from_dict(
+ rockcraft_data, supported_arches
+ )
>>>>>>> lib/lp/rocks/adapters/buildarch.py
# Ensure that multiple `run-on` items don't overlap; this is ambiguous
# and forbidden by rockcraft.
diff --git a/lib/lp/rocks/tests/test_rockrecipe.py b/lib/lp/rocks/tests/test_rockrecipe.py
index 8c41291..7e39d08 100644
--- a/lib/lp/rocks/tests/test_rockrecipe.py
+++ b/lib/lp/rocks/tests/test_rockrecipe.py
@@ -29,6 +29,10 @@ from testtools.matchers import (
MatchesSetwise,
MatchesStructure,
)
+<<<<<<< lib/lp/rocks/tests/test_rockrecipe.py
+=======
+from testtools.testcase import ExpectedException
+>>>>>>> lib/lp/rocks/tests/test_rockrecipe.py
from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
@@ -59,6 +63,7 @@ from lp.buildmaster.model.buildfarmjob import BuildFarmJob
from lp.buildmaster.model.buildqueue import BuildQueue
from lp.code.tests.helpers import GitHostingFixture
from lp.registry.enums import PersonVisibility, TeamMembershipPolicy
+from lp.rocks.adapters.buildarch import BadPropertyError, MissingPropertyError
from lp.rocks.interfaces.rockrecipe import (
ROCK_RECIPE_ALLOW_CREATE,
ROCK_RECIPE_PRIVATE_FEATURE_FLAG,
@@ -455,6 +460,276 @@ class TestRockRecipe(TestCaseWithFactory):
builds, job, "20.04", ["sparc", "avr"], job.channels
)
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_invalid_short_base(
+ self,
+ ):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base: ubuntu-24.04
+ platforms:
+ ubuntu-amd64:
+ build-on: [amd64]
+ build-for: [amd64]
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ with ExpectedException(
+ BadPropertyError,
+ "Invalid value for base 'ubuntu-24.04'. "
+ "Expected value should be like 'ubuntu@24.04'",
+ ):
+ job.recipe.requestBuildsFromJob(
+ job.build_request,
+ channels=removeSecurityProxy(job.channels),
+ )
+
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_platforms_missing(
+ self,
+ ):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base: ubuntu@24.04
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ with ExpectedException(
+ MissingPropertyError, "The 'platforms' property is required"
+ ):
+ job.recipe.requestBuildsFromJob(
+ job.build_request,
+ channels=removeSecurityProxy(job.channels),
+ )
+
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_fully_expanded(self):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base:
+ name: ubuntu
+ channel: 24.04
+ platforms:
+ ubuntu-amd64:
+ build-on: [amd64]
+ build-for: [amd64]
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ builds = job.recipe.requestBuildsFromJob(
+ job.build_request, channels=removeSecurityProxy(job.channels)
+ )
+ self.assertRequestedBuildsMatch(
+ builds, job, "24.04", ["amd64"], job.channels
+ )
+
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_multi_platforms(
+ self,
+ ):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base:
+ name: ubuntu
+ channel: 24.04
+ platforms:
+ ubuntu-amd64:
+ build-on: [amd64]
+ build-for: [amd64]
+ ubuntu-arm64:
+ build-on: [arm64]
+ build-for: [arm64]
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ builds = job.recipe.requestBuildsFromJob(
+ job.build_request, channels=removeSecurityProxy(job.channels)
+ )
+ self.assertRequestedBuildsMatch(
+ builds, job, "24.04", ["amd64", "arm64"], job.channels
+ )
+
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_arch_as_str(self):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base:
+ name: ubuntu
+ channel: 24.04
+ platforms:
+ ubuntu-amd64:
+ build-on: amd64
+ build-for: amd64
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ builds = job.recipe.requestBuildsFromJob(
+ job.build_request, channels=removeSecurityProxy(job.channels)
+ )
+ self.assertRequestedBuildsMatch(
+ builds, job, "24.04", ["amd64"], job.channels
+ )
+
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_base_short_form(
+ self,
+ ):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base: ubuntu@24.04
+ platforms:
+ ubuntu-amd64:
+ build-on: [amd64]
+ build-for: [amd64]
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ builds = job.recipe.requestBuildsFromJob(
+ job.build_request, channels=removeSecurityProxy(job.channels)
+ )
+ self.assertRequestedBuildsMatch(
+ builds, job, "24.04", ["amd64"], job.channels
+ )
+
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_unknown_arch(self):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base:
+ name: ubuntu
+ channel: 24.04
+ platforms:
+ foobar:
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ with ExpectedException(
+ BadPropertyError,
+ "'foobar' is not a supported architecture for "
+ "'ubuntu@24.04'",
+ ):
+ job.recipe.requestBuildsFromJob(
+ job.build_request,
+ channels=removeSecurityProxy(job.channels),
+ )
+
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_platforms_short_form(
+ self,
+ ):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base:
+ name: ubuntu
+ channel: 24.04
+ platforms:
+ amd64:
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ builds = job.recipe.requestBuildsFromJob(
+ job.build_request, channels=removeSecurityProxy(job.channels)
+ )
+ self.assertRequestedBuildsMatch(
+ builds, job, "24.04", ["amd64"], job.channels
+ )
+
+ def test_requestBuildsFromJob_unified_rockcraft_yaml_2_platforms_short(
+ self,
+ ):
+ self.useFixture(
+ GitHostingFixture(
+ blob=dedent(
+ """\
+ base:
+ name: ubuntu
+ channel: 24.04
+ platforms:
+ amd64:
+ arm64:
+ """
+ )
+ )
+ )
+ job = self.makeRequestBuildsJob("24.04", ["amd64", "riscv64", "arm64"])
+ self.assertEqual(
+ get_transaction_timestamp(IStore(job.recipe)), job.date_created
+ )
+ transaction.commit()
+ with person_logged_in(job.requester):
+ builds = job.recipe.requestBuildsFromJob(
+ job.build_request, channels=removeSecurityProxy(job.channels)
+ )
+ self.assertRequestedBuildsMatch(
+ builds, job, "24.04", ["amd64", "arm64"], job.channels
+ )
+
def test_requestBuildsFromJob_architectures_parameter(self):
# If an explicit set of architectures was given as a parameter,
# requestBuildsFromJob intersects those with any other constraints
Follow ups