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.
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp