launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #31624
[Merge] ~ruinedyourlife/launchpad:requesting-subset-of-arch-craft-builds into launchpad:master
Quentin Debhi has proposed merging ~ruinedyourlife/launchpad:requesting-subset-of-arch-craft-builds into launchpad:master with ~ruinedyourlife/launchpad:add-webservice-for-craft-recipes as a prerequisite.
Commit message:
Enable requesting a subset of architectures for craft builds
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~ruinedyourlife/launchpad/+git/launchpad/+merge/474360
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~ruinedyourlife/launchpad:requesting-subset-of-arch-craft-builds into launchpad:master.
diff --git a/lib/lp/crafts/interfaces/craftrecipe.py b/lib/lp/crafts/interfaces/craftrecipe.py
index 30be82e..1290e2e 100644
--- a/lib/lp/crafts/interfaces/craftrecipe.py
+++ b/lib/lp/crafts/interfaces/craftrecipe.py
@@ -69,6 +69,7 @@ from lp.app.errors import NameLookupFailed
from lp.app.interfaces.informationtype import IInformationType
from lp.app.validators.name import name_validator
from lp.app.validators.path import path_does_not_escape
+from lp.buildmaster.interfaces.processor import IProcessor
from lp.code.interfaces.gitref import IGitRef
from lp.code.interfaces.gitrepository import IGitRepository
from lp.registry.interfaces.person import IPerson
@@ -378,7 +379,12 @@ class ICraftRecipeView(Interface):
),
key_type=TextLine(),
required=False,
- )
+ ),
+ architectures=List(
+ title=_("The list of architectures to build for this recipe."),
+ value_type=Reference(schema=IProcessor),
+ required=False,
+ ),
)
@export_factory_operation(ICraftRecipeBuildRequest, [])
@operation_for_version("devel")
diff --git a/lib/lp/crafts/model/craftrecipe.py b/lib/lp/crafts/model/craftrecipe.py
index cd2203c..1368194 100644
--- a/lib/lp/crafts/model/craftrecipe.py
+++ b/lib/lp/crafts/model/craftrecipe.py
@@ -490,23 +490,16 @@ class CraftRecipe(StormBase):
# Sort by (Distribution.id, DistroSeries.id, Processor.id) for
# determinism. This is chosen to be a similar order as in
# BinaryPackageBuildSet.createForSource, to minimize confusion.
- supported_arches = [
- das
- for das in sorted(
- self.getAllowedArchitectures(),
- key=attrgetter(
- "distroseries.distribution.id",
- "distroseries.id",
- "processor.id",
- ),
- )
- if (
- architectures is None
- or das.architecturetag in architectures
- )
- ]
+ supported_arches = sorted(
+ self.getAllowedArchitectures(),
+ key=attrgetter(
+ "distroseries.distribution.id",
+ "distroseries.id",
+ "processor.id",
+ ),
+ )
instances_to_build = determine_instances_to_build(
- sourcecraft_data, supported_arches
+ sourcecraft_data, supported_arches, architectures
)
except Exception as e:
if not allow_failures:
diff --git a/lib/lp/crafts/model/craftrecipejob.py b/lib/lp/crafts/model/craftrecipejob.py
index 799e6e7..d573665 100644
--- a/lib/lp/crafts/model/craftrecipejob.py
+++ b/lib/lp/crafts/model/craftrecipejob.py
@@ -174,6 +174,14 @@ class CraftRecipeRequestBuildsJob(CraftRecipeJobDerived):
@classmethod
def create(cls, recipe, requester, channels=None, architectures=None):
"""See `ICraftRecipeRequestBuildsJobSource`."""
+ # architectures can be a iterable of strings or Processors
+ # in the latter case, we need to convert them to strings
+ if architectures and all(
+ not isinstance(arch, str) for arch in architectures
+ ):
+ architectures = [
+ architecture.name for architecture in architectures
+ ]
metadata = {
"requester": requester.id,
"channels": channels,
diff --git a/lib/lp/crafts/tests/test_craftrecipe.py b/lib/lp/crafts/tests/test_craftrecipe.py
index eac12e9..aaa2316 100644
--- a/lib/lp/crafts/tests/test_craftrecipe.py
+++ b/lib/lp/crafts/tests/test_craftrecipe.py
@@ -1497,6 +1497,130 @@ class TestCraftRecipeWebservice(TestCaseWithFactory):
),
)
+ def test_requestBuilds_with_architectures(self):
+ # when a subset of architectures are requested, we only build them
+ # not all listed in the sourcecraft.yaml file
+ distroseries = self.factory.makeDistroSeries(
+ distribution=getUtility(ILaunchpadCelebrities).ubuntu,
+ registrant=self.person,
+ )
+ amd640 = self.factory.makeProcessor(
+ name="amd640", supports_virtualized=True
+ )
+ risc500 = self.factory.makeProcessor(
+ name="risc500", supports_virtualized=True
+ )
+ s400x = self.factory.makeProcessor(
+ name="s400x", supports_virtualized=True
+ )
+ processors = [amd640, risc500, s400x]
+ for processor in processors:
+ self.makeBuildableDistroArchSeries(
+ distroseries=distroseries,
+ architecturetag=processor.name,
+ processor=processor,
+ owner=self.person,
+ )
+ [git_ref] = self.factory.makeGitRefs()
+ recipe = self.makeCraftRecipe(git_ref=git_ref)
+ now = get_transaction_timestamp(IStore(distroseries))
+ # api_base = "http://api.launchpad.test/devel"
+ # amd640_api_url = api_base api_url(amd640)
+ response = self.webservice.named_post(
+ recipe["self_link"],
+ "requestBuilds",
+ channels={"sourcecraft": "edge"},
+ architectures=[api_url(amd640), api_url(risc500)],
+ )
+ self.assertEqual(201, response.status)
+ build_request_url = response.getHeader("Location")
+ build_request = self.webservice.get(build_request_url).jsonBody()
+ self.assertThat(
+ build_request,
+ ContainsDict(
+ {
+ "date_requested": AfterPreprocessing(
+ iso8601.parse_date, GreaterThan(now)
+ ),
+ "date_finished": Is(None),
+ "recipe_link": Equals(recipe["self_link"]),
+ "status": Equals("Pending"),
+ "error_message": Is(None),
+ "builds_collection_link": Equals(
+ build_request_url + "/builds"
+ ),
+ }
+ ),
+ )
+ self.assertEqual([], self.getCollectionLinks(build_request, "builds"))
+ with person_logged_in(self.person):
+ sourcecraft_yaml = f"""
+ name: ruff
+ license: MIT
+ version: 0.4.9
+ summary: An extremely fast Python linter, written in Rust.
+ description: Ruff aims to be orders of magnitude faster...
+ base: ubuntu@{distroseries.version}
+ platforms:
+ """
+ for processor in processors:
+ sourcecraft_yaml += f"""
+ {processor.name}:
+ build-on: [{processor.name}]
+ build-for: {processor.name}
+ """
+ self.useFixture(GitHostingFixture(blob=sourcecraft_yaml))
+ [job] = getUtility(ICraftRecipeRequestBuildsJobSource).iterReady()
+ with dbuser(config.ICraftRecipeRequestBuildsJobSource.dbuser):
+ JobRunner([job]).runAll()
+ date_requested = iso8601.parse_date(build_request["date_requested"])
+ now = get_transaction_timestamp(IStore(distroseries))
+ build_request = self.webservice.get(
+ build_request["self_link"]
+ ).jsonBody()
+ self.assertThat(
+ build_request,
+ ContainsDict(
+ {
+ "date_requested": AfterPreprocessing(
+ iso8601.parse_date, Equals(date_requested)
+ ),
+ "date_finished": AfterPreprocessing(
+ iso8601.parse_date,
+ MatchesAll(GreaterThan(date_requested), LessThan(now)),
+ ),
+ "recipe_link": Equals(recipe["self_link"]),
+ "status": Equals("Completed"),
+ "error_message": Is(None),
+ "builds_collection_link": Equals(
+ build_request_url + "/builds"
+ ),
+ }
+ ),
+ )
+ builds = self.webservice.get(
+ build_request["builds_collection_link"]
+ ).jsonBody()["entries"]
+ with person_logged_in(self.person):
+ self.assertThat(
+ builds,
+ MatchesSetwise(
+ *(
+ ContainsDict(
+ {
+ "recipe_link": Equals(recipe["self_link"]),
+ "archive_link": Equals(
+ self.getURL(distroseries.main_archive)
+ ),
+ "arch_tag": Equals(processor.name),
+ "channels": Equals({"sourcecraft": "edge"}),
+ }
+ )
+ for processor in (amd640, risc500) # requested arches
+ )
+ ),
+ )
+
def test_getBuilds(self):
# The builds, completed_builds, and pending_builds properties are as
# expected.
Follow ups