launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #31630
[Merge] ~jugmac00/launchpad:expose-git_path-and-git_repository_url-via-api into launchpad:master
Jürgen Gmach has proposed merging ~jugmac00/launchpad:expose-git_path-and-git_repository_url-via-api into launchpad:master.
Commit message:
[WIP] Expose git_path and git_repository_url for rock recipes
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jugmac00/launchpad/+git/launchpad/+merge/474463
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~jugmac00/launchpad:expose-git_path-and-git_repository_url-via-api into launchpad:master.
diff --git a/lib/lp/rocks/interfaces/rockrecipe.py b/lib/lp/rocks/interfaces/rockrecipe.py
index ca87032..152f965 100644
--- a/lib/lp/rocks/interfaces/rockrecipe.py
+++ b/lib/lp/rocks/interfaces/rockrecipe.py
@@ -74,7 +74,7 @@ from lp.code.interfaces.gitref import IGitRef
from lp.code.interfaces.gitrepository import IGitRepository
from lp.registry.interfaces.person import IPerson
from lp.registry.interfaces.product import IProduct
-from lp.services.fields import PersonChoice, PublicPersonChoice
+from lp.services.fields import PersonChoice, PublicPersonChoice, URIField
from lp.snappy.validators.channels import channels_validator
ROCK_RECIPE_ALLOW_CREATE = "rock.recipe.create.enabled"
@@ -558,25 +558,48 @@ class IRockRecipeEditableAttributes(Interface):
)
)
- git_repository = ReferenceChoice(
- title=_("Git repository"),
- schema=IGitRepository,
- vocabulary="GitRepository",
- required=False,
- readonly=True,
- description=_(
- "A Git repository with a branch containing a rockcraft.yaml "
- "recipe."
- ),
+ git_repository = exported(
+ ReferenceChoice(
+ title=_("Git repository"),
+ schema=IGitRepository,
+ vocabulary="GitRepository",
+ required=False,
+ readonly=True,
+ description=_(
+ "A Git repository with a branch containing a rockcraft.yaml "
+ "recipe."
+ ),
+ )
)
- git_path = TextLine(
- title=_("Git branch path"),
- required=False,
- readonly=True,
- description=_(
- "The path of the Git branch containing a rockcraft.yaml recipe."
- ),
+ git_path = exported(
+ TextLine(
+ title=_("Git branch path"),
+ required=False,
+ readonly=True,
+ description=_(
+ "The path of the Git branch containing a rockcraft.yaml "
+ "recipe."
+ ),
+ )
+ )
+
+ git_repository_url = exported(
+ URIField(
+ title=_("Git repository URL"),
+ required=False,
+ readonly=True,
+ description=_(
+ "The URL of a Git repository with a branch containing a "
+ "rockcraft.yaml at the top level."
+ ),
+ allowed_schemes=["git", "http", "https"],
+ allow_userinfo=True,
+ allow_port=True,
+ allow_query=False,
+ allow_fragment=False,
+ trailing_slash=False,
+ )
)
git_ref = exported(
@@ -758,6 +781,9 @@ class IRockRecipeSet(Interface):
"project",
"name",
"description",
+ "git_repository",
+ "git_repository_url",
+ "git_path",
"git_ref",
"build_path",
"auto_build",
@@ -774,6 +800,9 @@ class IRockRecipeSet(Interface):
project,
name,
description=None,
+ git_repository=None,
+ git_repository_url=None,
+ git_path=None,
git_ref=None,
build_path=None,
require_virtualized=True,
diff --git a/lib/lp/rocks/model/rockrecipe.py b/lib/lp/rocks/model/rockrecipe.py
index 48a45e6..e0dd4c2 100644
--- a/lib/lp/rocks/model/rockrecipe.py
+++ b/lib/lp/rocks/model/rockrecipe.py
@@ -48,7 +48,7 @@ from lp.code.interfaces.gitcollection import (
IAllGitRepositories,
IGitCollection,
)
-from lp.code.interfaces.gitref import IGitRef
+from lp.code.interfaces.gitref import IGitRef, IGitRefRemoteSet
from lp.code.interfaces.gitrepository import IGitRepository
from lp.code.model.gitcollection import GenericGitCollection
from lp.code.model.gitref import GitRef
@@ -105,6 +105,7 @@ from lp.services.job.interfaces.job import JobStatus
from lp.services.job.model.job import Job
from lp.services.librarian.model import LibraryFileAlias
from lp.services.propertycache import cachedproperty, get_property_cache
+from lp.soyuz.interfaces.buildrecords import IncompatibleArguments
from lp.soyuz.model.distroarchseries import DistroArchSeries, PocketChroot
@@ -242,6 +243,8 @@ class RockRecipe(StormBase):
)
git_repository = Reference(git_repository_id, "GitRepository.id")
+ git_repository_url = Unicode(name="git_repository_url", allow_none=True)
+
git_path = Unicode(name="git_path", allow_none=True)
build_path = Unicode(name="build_path", allow_none=True)
@@ -341,6 +344,10 @@ class RockRecipe(StormBase):
def _git_ref(self):
if self.git_repository is not None:
return self.git_repository.getRefByPath(self.git_path)
+ elif self.git_repository_url is not None:
+ return getUtility(IGitRefRemoteSet).new(
+ self.git_repository_url, self.git_path
+ )
else:
return None
@@ -354,9 +361,11 @@ class RockRecipe(StormBase):
"""See `IRockRecipe`."""
if value is not None:
self.git_repository = value.repository
+ self.git_repository_url = value.repository_url
self.git_path = value.path
else:
self.git_repository = None
+ self.git_repository_url = None
self.git_path = None
get_property_cache(self)._git_ref = value
@@ -767,6 +776,9 @@ class RockRecipeSet:
project,
name,
description=None,
+ git_repository=None,
+ git_repository_url=None,
+ git_path=None,
git_ref=None,
build_path=None,
require_virtualized=True,
@@ -793,6 +805,34 @@ class RockRecipeSet:
% (registrant.displayname, owner.displayname)
)
+ if (
+ sum(
+ [
+ git_repository is not None,
+ git_repository_url is not None,
+ git_ref is not None,
+ ]
+ )
+ > 1
+ ):
+ raise IncompatibleArguments(
+ "You cannot specify more than one of 'git_repository', "
+ "'git_repository_url', and 'git_ref'."
+ )
+ if (git_repository is None and git_repository_url is None) != (
+ git_path is None
+ ):
+ raise IncompatibleArguments(
+ "You must specify both or neither of "
+ "'git_repository'/'git_repository_url' and 'git_path'."
+ )
+ if git_repository is not None:
+ git_ref = git_repository.getRefByPath(git_path)
+ elif git_repository_url is not None:
+ git_ref = getUtility(IGitRefRemoteSet).new(
+ git_repository_url, git_path
+ )
+
if git_ref is None:
raise NoSourceForRockRecipe
if self.exists(owner, project, name):
@@ -873,7 +913,12 @@ class RockRecipeSet:
git_collection = removeSecurityProxy(getUtility(IAllGitRepositories))
git_recipes = _getRecipes(git_collection)
- return git_recipes
+ git_url_recipes = IStore(RockRecipe).find(
+ RockRecipe,
+ RockRecipe.owner == person,
+ RockRecipe.git_repository_url != None,
+ )
+ return git_recipes.union(git_url_recipes)
def findByProject(self, project, visible_by_user=None):
"""See `IRockRecipeSet`."""
diff --git a/lib/lp/rocks/model/rockrecipebuildbehaviour.py b/lib/lp/rocks/model/rockrecipebuildbehaviour.py
index a3bf315..fa5858e 100644
--- a/lib/lp/rocks/model/rockrecipebuildbehaviour.py
+++ b/lib/lp/rocks/model/rockrecipebuildbehaviour.py
@@ -96,7 +96,12 @@ class RockRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
if build.recipe.build_path is not None:
args["build_path"] = build.recipe.build_path
if build.recipe.git_ref is not None:
- args["git_repository"] = build.recipe.git_repository.git_https_url
+ if build.recipe.git_ref.repository_url is not None:
+ args["git_repository"] = build.recipe.git_ref.repository_url
+ else:
+ args["git_repository"] = (
+ build.snap.git_repository.git_https_url
+ )
# "git clone -b" doesn't accept full ref names. If this becomes
# a problem then we could change launchpad-buildd to do "git
# clone" followed by "git checkout" instead.
diff --git a/lib/lp/rocks/tests/test_rockrecipe.py b/lib/lp/rocks/tests/test_rockrecipe.py
index 1b4dc47..019d704 100644
--- a/lib/lp/rocks/tests/test_rockrecipe.py
+++ b/lib/lp/rocks/tests/test_rockrecipe.py
@@ -1148,6 +1148,18 @@ class TestRockRecipeSet(TestCaseWithFactory):
self.assertEqual([], recipe.store_channels)
self.assertFalse(recipe.use_fetch_service)
+ def test_creation_git_url(self):
+ # A rock recipe can be backed directly by a URL for an external Git
+ # repository, rather than a Git repository hosted in Launchpad.
+ ref = self.factory.makeGitRefRemote()
+ components = self.makeRockRecipeComponents(git_ref=ref)
+ rock_recipe = getUtility(IRockRecipeSet).new(**components)
+ self.assertIsNone(rock_recipe.branch)
+ self.assertIsNone(rock_recipe.git_repository)
+ self.assertEqual(ref.repository_url, rock_recipe.git_repository_url)
+ self.assertEqual(ref.path, rock_recipe.git_path)
+ self.assertEqual(ref, rock_recipe.git_ref)
+
def test_creation_no_source(self):
# Attempting to create a rock recipe without a Git repository
# fails.
diff --git a/lib/lp/rocks/tests/test_rockrecipebuildbehaviour.py b/lib/lp/rocks/tests/test_rockrecipebuildbehaviour.py
index f2b8bac..45fcd2a 100644
--- a/lib/lp/rocks/tests/test_rockrecipebuildbehaviour.py
+++ b/lib/lp/rocks/tests/test_rockrecipebuildbehaviour.py
@@ -460,6 +460,52 @@ class TestAsyncRockRecipeBuildBehaviour(
self.assertTrue(args["private"])
@defer.inlineCallbacks
+ def test_extraBuildArgs_git_url(self):
+ # extraBuildArgs returns appropriate arguments if asked to build a
+ # job for a Git branch backed by a URL for an external repository.
+ url = "https://git.example.org/foo"
+ ref = self.factory.makeGitRefRemote(
+ repository_url=url, path="refs/heads/master"
+ )
+ job = self.makeJob(git_ref=ref)
+ (
+ expected_archives,
+ expected_trusted_keys,
+ ) = yield get_sources_list_for_building(
+ job, job.build.distro_arch_series, None
+ )
+ for archive_line in expected_archives:
+ self.assertIn("universe", archive_line)
+ with dbuser(config.builddmaster.dbuser):
+ args = yield job.extraBuildArgs()
+ self.assertThat(
+ args,
+ MatchesDict(
+ {
+ "archive_private": Is(False),
+ "archives": Equals(expected_archives),
+ "arch_tag": Equals("i386"),
+ "build_source_tarball": Is(False),
+ "build_url": Equals(canonical_url(job.build)),
+ "builder_constraints": Equals([]),
+ "fast_cleanup": Is(True),
+ "git_repository": Equals(url),
+ "git_path": Equals("master"),
+ "name": Equals("test-snap"),
+ "private": Is(False),
+ "proxy_url": ProxyURLMatcher(job, self.now),
+ "revocation_endpoint": RevocationEndpointMatcher(
+ job, self.now
+ ),
+ "series": Equals("unstable"),
+ "trusted_keys": Equals(expected_trusted_keys),
+ "target_architectures": Equals(["i386"]),
+ "use_fetch_service": Is(None),
+ }
+ ),
+ )
+
+ @defer.inlineCallbacks
def test_composeBuildRequest_proxy_url_set(self):
job = self.makeJob()
build_request = yield job.composeBuildRequest(None)
Follow ups