launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #19868
[Merge] lp:~cjwatson/launchpad/git-recipe-browser-create into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/git-recipe-browser-create into lp:launchpad with lp:~cjwatson/launchpad/git-recipe-browser-listing as a prerequisite.
Commit message:
Add views to create new Git recipes.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1453022 in Launchpad itself: "Please support daily builds via git"
https://bugs.launchpad.net/launchpad/+bug/1453022
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-recipe-browser-create/+merge/282324
Add views to create new Git recipes.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-recipe-browser-create into lp:launchpad.
=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml 2016-01-12 15:31:39 +0000
+++ lib/lp/code/browser/configure.zcml 2016-01-12 15:31:39 +0000
@@ -1219,6 +1219,18 @@
name="+new-recipe"
template="../templates/sourcepackagerecipe-new.pt"/>
<browser:page
+ for="lp.code.interfaces.gitrepository.IGitRepository"
+ class="lp.code.browser.sourcepackagerecipe.SourcePackageRecipeAddView"
+ permission="launchpad.AnyPerson"
+ name="+new-recipe"
+ template="../templates/sourcepackagerecipe-new.pt"/>
+ <browser:page
+ for="lp.code.interfaces.gitref.IGitRef"
+ class="lp.code.browser.sourcepackagerecipe.SourcePackageRecipeAddView"
+ permission="launchpad.AnyPerson"
+ name="+new-recipe"
+ template="../templates/sourcepackagerecipe-new.pt"/>
+ <browser:page
for="lp.code.interfaces.sourcepackagerecipe.ISourcePackageRecipe"
class="lp.code.browser.sourcepackagerecipe.SourcePackageRecipeEditView"
permission="launchpad.Edit"
=== modified file 'lib/lp/code/browser/gitref.py'
--- lib/lp/code/browser/gitref.py 2016-01-12 15:31:39 +0000
+++ lib/lp/code/browser/gitref.py 2016-01-12 15:31:39 +0000
@@ -62,7 +62,9 @@
usedfor = IGitRef
facet = 'branches'
- links = ['create_snap', 'register_merge', 'source', 'view_recipes']
+ links = [
+ 'create_recipe', 'create_snap', 'register_merge', 'source',
+ 'view_recipes']
def source(self):
"""Return a link to the branch's browsing interface."""
@@ -75,6 +77,12 @@
enabled = self.context.namespace.supports_merge_proposals
return Link('+register-merge', text, icon='add', enabled=enabled)
+ def create_recipe(self):
+ # You can't create a recipe for a reference in a private repository.
+ enabled = not self.context.private
+ text = "Create packaging recipe"
+ return Link("+new-recipe", text, enabled=enabled, icon="add")
+
class GitRefView(LaunchpadView, HasSnapsViewMixin):
=== modified file 'lib/lp/code/browser/gitrepository.py'
--- lib/lp/code/browser/gitrepository.py 2016-01-12 15:31:39 +0000
+++ lib/lp/code/browser/gitrepository.py 2016-01-12 15:31:39 +0000
@@ -210,7 +210,7 @@
usedfor = IGitRepository
facet = "branches"
links = [
- "add_subscriber", "source", "subscription",
+ "add_subscriber", "create_recipe", "source", "subscription",
"view_recipes", "visibility"]
@enabled_with_permission("launchpad.AnyPerson")
@@ -242,6 +242,12 @@
text = "Change information type"
return Link("+edit-information-type", text)
+ def create_recipe(self):
+ # You can't create a recipe for a private repository.
+ enabled = not self.context.private
+ text = "Create packaging recipe"
+ return Link("+new-recipe", text, enabled=enabled, icon="add")
+
@implementer(IGitRefBatchNavigator)
class GitRefBatchNavigator(TableBatchNavigator):
=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
--- lib/lp/code/browser/sourcepackagerecipe.py 2016-01-12 15:31:39 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py 2016-01-12 15:31:39 +0000
@@ -92,11 +92,14 @@
)
from lp.code.interfaces.branch import IBranch
from lp.code.interfaces.branchtarget import IBranchTarget
+from lp.code.interfaces.gitref import IGitRef
+from lp.code.interfaces.gitrepository import IGitRepository
from lp.code.interfaces.sourcepackagerecipe import (
IRecipeBranchSource,
ISourcePackageRecipe,
ISourcePackageRecipeSource,
MINIMAL_RECIPE_TEXT_BZR,
+ MINIMAL_RECIPE_TEXT_GIT,
)
from lp.code.vocabularies.sourcepackagerecipe import BuildableDistroSeries
from lp.registry.interfaces.series import SeriesStatus
@@ -737,14 +740,17 @@
self.form_fields['distroseries'].for_input = True
def getBranch(self):
- """The branch on which the recipe is built."""
+ """The branch or repository on which the recipe is built."""
return self.context
def _recipe_names(self):
"""A generator of recipe names."""
# +junk-daily doesn't make a very good recipe name, so use the
- # branch name in that case.
- if self.context.target.allow_recipe_name_from_target:
+ # branch name in that case; similarly for personal Git repositories.
+ if ((IBranch.providedBy(self.context) and
+ self.context.target.allow_recipe_name_from_target) or
+ (IGitRepository.providedBy(self.context) and
+ self.context.namespace.allow_recipe_name_from_target)):
branch_target_name = self.context.target.name.split('/')[-1]
else:
branch_target_name = self.context.name
@@ -765,9 +771,27 @@
distroseries = BuildableDistroSeries.findSeries(self.user)
series = [series for series in distroseries if series.status in (
SeriesStatus.CURRENT, SeriesStatus.DEVELOPMENT)]
+ if IBranch.providedBy(self.context):
+ recipe_text = MINIMAL_RECIPE_TEXT_BZR % self.context.identity
+ elif IGitRepository.providedBy(self.context):
+ default_ref = None
+ if self.context.default_branch is not None:
+ default_ref = self.context.getRefByPath(
+ self.context.default_branch)
+ if default_ref is not None:
+ branch_name = default_ref.name
+ else:
+ branch_name = "ENTER-BRANCH-NAME"
+ recipe_text = MINIMAL_RECIPE_TEXT_GIT % (
+ self.context.identity, branch_name)
+ elif IGitRef.providedBy(self.context):
+ recipe_text = MINIMAL_RECIPE_TEXT_GIT % (
+ self.context.repository.identity, self.context.name)
+ else:
+ raise AssertionError("Unsupported context: %r" % (self.context,))
return {
'name': self._find_unused_name(self.user),
- 'recipe_text': MINIMAL_RECIPE_TEXT_BZR % self.context.bzr_identity,
+ 'recipe_text': recipe_text,
'owner': self.user,
'distroseries': series,
'build_daily': True,
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2016-01-12 15:31:39 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2016-01-12 15:31:39 +0000
@@ -265,36 +265,10 @@
daily_build_archive=self.ppa, **kwargs)
-class TestSourcePackageRecipeAddViewInitalValues(TestCaseWithFactory):
+class TestSourcePackageRecipeAddViewInitialValuesMixin:
layer = DatabaseFunctionalLayer
- def test_project_branch_initial_name(self):
- # When a project branch is used, the initial name is the name of the
- # project followed by "-daily"
- widget = self.factory.makeProduct(name='widget')
- branch = self.factory.makeProductBranch(widget)
- with person_logged_in(branch.owner):
- view = create_initialized_view(branch, '+new-recipe')
- self.assertThat('widget-daily', Equals(view.initial_values['name']))
-
- def test_package_branch_initial_name(self):
- # When a package branch is used, the initial name is the name of the
- # source package followed by "-daily"
- branch = self.factory.makePackageBranch(sourcepackagename='widget')
- with person_logged_in(branch.owner):
- view = create_initialized_view(branch, '+new-recipe')
- self.assertThat('widget-daily', Equals(view.initial_values['name']))
-
- def test_personal_branch_initial_name(self):
- # When a personal branch is used, the initial name is the name of the
- # branch followed by "-daily". +junk-daily is not valid nor
- # helpful.
- branch = self.factory.makePersonalBranch(name='widget')
- with person_logged_in(branch.owner):
- view = create_initialized_view(branch, '+new-recipe')
- self.assertThat('widget-daily', Equals(view.initial_values['name']))
-
def test_initial_name_exists(self):
# If the initial name exists, a generator is used to find an unused
# name by appending a numbered suffix on the end.
@@ -302,7 +276,7 @@
self.factory.makeSourcePackageRecipe(
owner=owner, name=u'widget-daily')
widget = self.factory.makeProduct(name='widget')
- branch = self.factory.makeProductBranch(widget)
+ branch = self.makeBranch(target=widget)
with person_logged_in(owner):
view = create_initialized_view(branch, '+new-recipe')
self.assertThat('widget-daily-1', Equals(view.initial_values['name']))
@@ -331,7 +305,7 @@
future = self.factory.makeDistroSeries(
distribution=archive.distribution,
status=SeriesStatus.FUTURE)
- branch = self.factory.makeAnyBranch()
+ branch = self.makeBranch()
with person_logged_in(archive.owner):
view = create_initialized_view(branch, '+new-recipe')
series = set(view.initial_values['distroseries'])
@@ -342,30 +316,88 @@
self.assertEqual(set(), series.intersection(other_series))
-class TestSourcePackageRecipeAddView(BzrMixin, TestCaseForRecipe):
+class TestSourcePackageRecipeAddViewInitialValuesBzr(
+ TestSourcePackageRecipeAddViewInitialValuesMixin, BzrMixin,
+ TestCaseWithFactory):
+
+ def test_project_branch_initial_name(self):
+ # When a project branch is used, the initial name is the name of the
+ # project followed by "-daily".
+ widget = self.factory.makeProduct(name='widget')
+ branch = self.factory.makeProductBranch(widget)
+ with person_logged_in(branch.owner):
+ view = create_initialized_view(branch, '+new-recipe')
+ self.assertThat('widget-daily', Equals(view.initial_values['name']))
+
+ def test_package_branch_initial_name(self):
+ # When a package branch is used, the initial name is the name of the
+ # source package followed by "-daily".
+ branch = self.factory.makePackageBranch(sourcepackagename='widget')
+ with person_logged_in(branch.owner):
+ view = create_initialized_view(branch, '+new-recipe')
+ self.assertThat('widget-daily', Equals(view.initial_values['name']))
+
+ def test_personal_branch_initial_name(self):
+ # When a personal branch is used, the initial name is the name of
+ # the branch followed by "-daily". +junk-daily is neither valid nor
+ # helpful.
+ branch = self.factory.makePersonalBranch(name='widget')
+ with person_logged_in(branch.owner):
+ view = create_initialized_view(branch, '+new-recipe')
+ self.assertThat('widget-daily', Equals(view.initial_values['name']))
+
+
+class TestSourcePackageRecipeAddViewInitialValuesGit(
+ TestSourcePackageRecipeAddViewInitialValuesMixin, GitMixin,
+ TestCaseWithFactory):
+
+ def test_project_repository_initial_name(self):
+ # When a project repository is used, the initial name is the name of
+ # the project followed by "-daily".
+ widget = self.factory.makeProduct(name='widget')
+ repository = self.factory.makeGitRepository(target=widget)
+ with person_logged_in(repository.owner):
+ view = create_initialized_view(repository, '+new-recipe')
+ self.assertThat('widget-daily', Equals(view.initial_values['name']))
+
+ def test_package_repository_initial_name(self):
+ # When a package repository is used, the initial name is the name of
+ # the source package followed by "-daily".
+ dsp = self.factory.makeDistributionSourcePackage(
+ sourcepackagename='widget')
+ repository = self.factory.makeGitRepository(target=dsp)
+ with person_logged_in(repository.owner):
+ view = create_initialized_view(repository, '+new-recipe')
+ self.assertThat('widget-daily', Equals(view.initial_values['name']))
+
+ def test_personal_repository_initial_name(self):
+ # When a personal repository is used, the initial name is the name
+ # of the repository followed by "-daily". <person-name>-daily is
+ # not helpful.
+ owner = self.factory.makePerson()
+ repository = self.factory.makeGitRepository(
+ owner=owner, target=owner, name=u'widget')
+ with person_logged_in(repository.owner):
+ view = create_initialized_view(repository, '+new-recipe')
+ self.assertThat('widget-daily', Equals(view.initial_values['name']))
+
+
+class TestSourcePackageRecipeAddViewMixin:
layer = DatabaseFunctionalLayer
- def makeBranch(self):
- product = self.factory.makeProduct(
- name='ratatouille', displayname='Ratatouille')
- branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
- self.factory.makeSourcePackage(sourcepackagename='ratatouille')
- return branch
-
def test_create_new_recipe_not_logged_in(self):
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
+ branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
browser = self.getViewBrowser(branch, no_login=True)
self.assertRaises(
Unauthorized, browser.getLink('Create packaging recipe').click)
def test_create_new_recipe(self):
- branch = self.makeBranch()
+ branch = self.makeBranchAndPackage()
# A new recipe can be created from the branch page.
browser = self.getUserBrowser(canonical_url(branch), user=self.chef)
browser.getLink('Create packaging recipe').click()
@@ -388,7 +420,7 @@
def test_create_new_recipe_private_branch(self):
# Recipes can't be created on private branches.
with person_logged_in(self.chef):
- branch = self.factory.makeBranch(
+ branch = self.makeBranch(
owner=self.chef, information_type=InformationType.USERDATA)
branch_url = canonical_url(branch)
@@ -403,7 +435,7 @@
self.factory.makeTeam(
name='good-chefs', displayname='Good Chefs', members=[self.chef])
browser = self.getViewBrowser(
- self.makeBranch(), '+new-recipe', user=self.chef)
+ self.makeBranchAndPackage(), '+new-recipe', user=self.chef)
# The options for the owner include the Good Chefs team.
options = browser.getControl(name='field.owner.owner').displayOptions
self.assertEquals(
@@ -415,7 +447,7 @@
team = self.factory.makeTeam(
name='good-chefs', displayname='Good Chefs', members=[self.chef])
browser = self.getViewBrowser(
- self.makeBranch(), '+new-recipe', user=self.chef)
+ self.makeBranchAndPackage(), '+new-recipe', user=self.chef)
browser.getControl(name='field.name').value = 'daily'
browser.getControl('Description').value = 'Make some food!'
browser.getControl('Other').click()
@@ -430,7 +462,7 @@
def test_create_new_recipe_suggests_user(self):
"""The current user is suggested as a recipe owner, once."""
- branch = self.factory.makeBranch(owner=self.chef)
+ branch = self.makeBranch(owner=self.chef)
text = self.getMainText(branch, '+new-recipe')
self.assertTextMatchesExpressionIgnoreWhitespace(
r'Owner: Master Chef \(chef\) Other:', text)
@@ -440,7 +472,7 @@
team = self.factory.makeTeam(
name='branch-team', displayname='Branch Team',
members=[self.chef])
- branch = self.factory.makeBranch(owner=team)
+ branch = self.makeBranch(owner=team)
text = self.getMainText(branch, '+new-recipe')
self.assertTextMatchesExpressionIgnoreWhitespace(
r'Owner: Master Chef \(chef\)'
@@ -450,7 +482,7 @@
"""If current user isn't a member of branch owner, it is ignored."""
team = self.factory.makeTeam(
name='branch-team', displayname='Branch Team')
- branch = self.factory.makeBranch(owner=team)
+ branch = self.makeBranch(owner=team)
text = self.getMainText(branch, '+new-recipe')
self.assertTextMatchesExpressionIgnoreWhitespace(
r'Owner: Master Chef \(chef\) Other:', text)
@@ -460,8 +492,8 @@
# is communicated to the user properly.
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
+ branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
browser = self.getViewBrowser(branch, '+new-recipe', user=self.chef)
browser.getControl('Description').value = 'Make some food!'
browser.getControl('Recipe text').value = (
@@ -475,8 +507,8 @@
if branch is None:
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
+ branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
browser = self.getViewBrowser(branch, '+new-recipe', user=self.chef)
browser.getControl(name='field.name').value = 'daily'
browser.getControl('Description').value = 'Make some food!'
@@ -487,18 +519,11 @@
def test_create_recipe_usage(self):
# The error for a recipe with invalid instruction parameters should
# include instruction usage.
- branch = self.factory.makeBranch(name='veggies')
- self.factory.makeBranch(name='packaging')
+ branch = self.makeBranch(name=u'veggies')
+ self.makeBranch(name=u'packaging')
browser = self.createRecipe(
- dedent('''\
- # bzr-builder format 0.2 deb-version 0+{revno}
- %(branch)s
- merge
- ''' % {
- 'branch': branch.bzr_identity,
- }),
- branch=branch)
+ self.getMinimalRecipeText(branch) + "merge\n", branch=branch)
self.assertEqual(
'Error parsing recipe:3:6: '
'End of line while looking for the branch id.\n'
@@ -506,7 +531,8 @@
get_feedback_messages(browser.contents)[1])
def test_create_recipe_no_distroseries(self):
- browser = self.getViewBrowser(self.makeBranch(), '+new-recipe')
+ browser = self.getViewBrowser(
+ self.makeBranchAndPackage(), '+new-recipe')
browser.getControl(name='field.name').value = 'daily'
browser.getControl('Description').value = 'Make some food!'
browser.getControl(name='field.distroseries').value = []
@@ -518,7 +544,11 @@
def test_create_recipe_bad_base_branch(self):
# If a user tries to create source package recipe with a bad base
# branch location, they should get an error.
- browser = self.createRecipe(MINIMAL_RECIPE_TEXT_BZR % 'foo')
+ browser = self.createRecipe(
+ self.minimal_recipe_text.splitlines()[0] + '\nfoo\n')
+ # This page doesn't know whether the user was aiming for a Bazaar
+ # branch or a Git repository; the error message always says
+ # "branch".
self.assertEqual(
get_feedback_messages(browser.contents)[1],
'foo is not a branch on Launchpad.')
@@ -528,28 +558,27 @@
# instruction branch location, they should get an error.
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
- recipe = MINIMAL_RECIPE_TEXT_BZR % branch.bzr_identity
+ branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
+ recipe = self.getMinimalRecipeText(branch)
recipe += 'nest packaging foo debian'
browser = self.createRecipe(recipe, branch)
self.assertEqual(
get_feedback_messages(browser.contents)[1],
- 'foo is not a branch on Launchpad.')
+ 'foo %s' % self.no_such_object_message)
def test_create_recipe_format_too_new(self):
# If the recipe's format version is too new, we should notify the
# user.
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
+ branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
with recipe_parser_newest_version(145.115):
- recipe = dedent(u'''\
- # bzr-builder format 145.115 deb-version {debupstream}-0~{revno}
- %s
- ''') % branch.bzr_identity
+ recipe = re.sub(
+ 'format [^ ]*', 'format 145.115',
+ self.getMinimalRecipeText(branch))
browser = self.createRecipe(recipe, branch)
self.assertEqual(
get_feedback_messages(browser.contents)[1],
@@ -564,8 +593,8 @@
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
+ branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
# A new recipe can be created from the branch page.
browser = self.getUserBrowser(canonical_url(branch), user=self.chef)
@@ -583,15 +612,16 @@
def test_create_recipe_private_branch(self):
# If a user tries to create source package recipe with a private
# base branch, they should get an error.
- branch = self.factory.makeAnyBranch(
+ branch = self.makeBranch(
owner=self.user, information_type=InformationType.USERDATA)
with person_logged_in(self.user):
- bzr_identity = branch.bzr_identity
- recipe_text = MINIMAL_RECIPE_TEXT_BZR % bzr_identity
+ identity = self.getRepository(branch).identity
+ recipe_text = self.getMinimalRecipeText(branch)
browser = self.createRecipe(recipe_text)
self.assertEqual(
get_feedback_messages(browser.contents)[1],
- 'Recipe may not refer to private branch: %s' % bzr_identity)
+ 'Recipe may not refer to private %s: %s' % (
+ self.branch_type, identity))
def _test_new_recipe_with_no_related_branches(self, branch):
# The Related Branches section should not appear if there are no
@@ -607,72 +637,20 @@
def test_new_product_branch_with_no_related_branches_recipe(self):
# We can create a new recipe off a product branch.
- branch = self.factory.makeBranch()
+ branch = self.makeBranch()
self._test_new_recipe_with_no_related_branches(branch)
def test_new_package_branch_with_no_linked_branches_recipe(self):
# We can create a new recipe off a sourcepackage branch where the
# sourcepackage has no linked branches.
- branch = self.factory.makePackageBranch()
+ branch = self.makePackageBranch()
self._test_new_recipe_with_no_related_branches(branch)
- def test_new_recipe_with_package_branches(self):
- # The series branches table should not appear if there are none.
- (branch, related_series_branch_info, related_package_branches) = (
- self.factory.makeRelatedBranches(with_series_branches=False))
- browser = self.getUserBrowser(
- canonical_url(branch, view_name='+new-recipe'), user=self.chef)
- soup = BeautifulSoup(browser.contents)
- related_branches = soup.find('fieldset', {'id': 'related-branches'})
- self.assertIsNot(related_branches, None)
- related_branches = soup.find(
- 'div', {'id': 'related-package-branches'})
- self.assertIsNot(related_branches, None)
- related_branches = soup.find(
- 'div', {'id': 'related-series-branches'})
- self.assertIs(related_branches, None)
-
- def test_new_recipe_with_series_branches(self):
- # The package branches table should not appear if there are none.
- (branch, related_series_branch_info, related_package_branches) = (
- self.factory.makeRelatedBranches(with_package_branches=False))
- browser = self.getUserBrowser(
- canonical_url(branch, view_name='+new-recipe'), user=self.chef)
- soup = BeautifulSoup(browser.contents)
- related_branches = soup.find('fieldset', {'id': 'related-branches'})
- self.assertIsNot(related_branches, None)
- related_branches = soup.find(
- 'div', {'id': 'related-series-branches'})
- self.assertIsNot(related_branches, None)
- related_branches = soup.find(
- 'div', {'id': 'related-package-branches'})
- self.assertIs(related_branches, None)
-
- def test_new_product_branch_recipe_with_related_branches(self):
- # The related branches should be rendered correctly on the page.
- (branch, related_series_branch_info,
- related_package_branch_info) = self.factory.makeRelatedBranches()
- browser = self.getUserBrowser(
- canonical_url(branch, view_name='+new-recipe'), user=self.chef)
- self.checkRelatedBranches(
- related_series_branch_info, related_package_branch_info,
- browser.contents)
-
- def test_new_sourcepackage_branch_recipe_with_related_branches(self):
- # The related branches should be rendered correctly on the page.
- reference_branch = self.factory.makePackageBranch()
- (branch, ignore, related_package_branch_info) = (
- self.factory.makeRelatedBranches(reference_branch))
- browser = self.getUserBrowser(
- canonical_url(branch, view_name='+new-recipe'), user=self.chef)
- self.checkRelatedBranches(
- set(), related_package_branch_info, browser.contents)
-
def test_ppa_selector_not_shown_if_user_has_no_ppas(self):
# If the user creating a recipe has no existing PPAs, the selector
# isn't shown, but the field to enter a new PPA name is.
self.user = self.factory.makePerson()
- branch = self.factory.makeAnyBranch()
+ branch = self.makeBranch()
with person_logged_in(self.user):
content = self.getMainContent(branch, '+new-recipe')
ppa_name = content.find(attrs={'id': 'field.ppa_name'})
@@ -692,7 +670,7 @@
# If the user creating a recipe has existing PPAs, the selector is
# shown, along with radio buttons to decide whether to use an existing
# ppa or to create a new one.
- branch = self.factory.makeAnyBranch()
+ branch = self.makeBranch()
with person_logged_in(self.user):
content = self.getMainContent(branch, '+new-recipe')
ppa_name = content.find(attrs={'id': 'field.ppa_name'})
@@ -712,7 +690,7 @@
def test_create_new_ppa(self):
# If the user doesn't have any PPAs, a new once can be created.
self.user = self.factory.makePerson(name='eric')
- branch = self.factory.makeAnyBranch()
+ branch = self.makeBranch()
# A new recipe can be created from the branch page.
browser = self.getUserBrowser(canonical_url(branch), user=self.user)
@@ -737,7 +715,7 @@
# Make a PPA called 'ppa' using the default.
with person_logged_in(self.user):
self.user.createPPA(name='foo')
- branch = self.factory.makeAnyBranch()
+ branch = self.makeBranch()
# A new recipe can be created from the branch page.
browser = self.getUserBrowser(canonical_url(branch), user=self.user)
@@ -756,7 +734,7 @@
# If a new PPA is being created, and the user has not specified a
# name, an error is shown.
self.user = self.factory.makePerson(name='eric')
- branch = self.factory.makeAnyBranch()
+ branch = self.makeBranch()
# A new recipe can be created from the branch page.
browser = self.getUserBrowser(canonical_url(branch), user=self.user)
@@ -779,7 +757,7 @@
with person_logged_in(team.teamowner):
team.setMembershipData(
self.user, TeamMembershipStatus.ADMIN, team.teamowner)
- branch = self.factory.makeAnyBranch(owner=team)
+ branch = self.makeBranch(owner=team)
# A new recipe can be created from the branch page.
browser = self.getUserBrowser(canonical_url(branch), user=self.user)
@@ -799,6 +777,83 @@
self.assertIsNot(None, new_ppa)
+class TestSourcePackageRecipeAddViewBzr(
+ TestSourcePackageRecipeAddViewMixin, BzrMixin, TestCaseForRecipe):
+
+ def makeBranchAndPackage(self):
+ product = self.factory.makeProduct(
+ name='ratatouille', displayname='Ratatouille')
+ branch = self.factory.makeBranch(
+ owner=self.chef, product=product, name='veggies')
+ self.factory.makeSourcePackage(sourcepackagename='ratatouille')
+ return branch
+
+ def test_new_recipe_with_package_branches(self):
+ # The series branches table should not appear if there are none.
+ branch, related_series_branch_info, related_package_branches = (
+ self.makeRelatedBranches(with_series_branches=False))
+ browser = self.getUserBrowser(
+ canonical_url(branch, view_name='+new-recipe'), user=self.chef)
+ soup = BeautifulSoup(browser.contents)
+ related_branches = soup.find('fieldset', {'id': 'related-branches'})
+ self.assertIsNot(related_branches, None)
+ related_branches = soup.find(
+ 'div', {'id': 'related-package-branches'})
+ self.assertIsNot(related_branches, None)
+ related_branches = soup.find(
+ 'div', {'id': 'related-series-branches'})
+ self.assertIs(related_branches, None)
+
+ def test_new_recipe_with_series_branches(self):
+ # The package branches table should not appear if there are none.
+ branch, related_series_branch_info, related_package_branches = (
+ self.makeRelatedBranches(with_package_branches=False))
+ browser = self.getUserBrowser(
+ canonical_url(branch, view_name='+new-recipe'), user=self.chef)
+ soup = BeautifulSoup(browser.contents)
+ related_branches = soup.find('fieldset', {'id': 'related-branches'})
+ self.assertIsNot(related_branches, None)
+ related_branches = soup.find(
+ 'div', {'id': 'related-series-branches'})
+ self.assertIsNot(related_branches, None)
+ related_branches = soup.find(
+ 'div', {'id': 'related-package-branches'})
+ self.assertIs(related_branches, None)
+
+ def test_new_product_branch_recipe_with_related_branches(self):
+ # The related branches should be rendered correctly on the page.
+ branch, related_series_branch_info, related_package_branch_info = (
+ self.makeRelatedBranches())
+ browser = self.getUserBrowser(
+ canonical_url(branch, view_name='+new-recipe'), user=self.chef)
+ self.checkRelatedBranches(
+ related_series_branch_info, related_package_branch_info,
+ browser.contents)
+
+ def test_new_sourcepackage_branch_recipe_with_related_branches(self):
+ # The related branches should be rendered correctly on the page.
+ reference_branch = self.makePackageBranch()
+ branch, _, related_package_branch_info = (
+ self.makeRelatedBranches(reference_branch))
+ browser = self.getUserBrowser(
+ canonical_url(branch, view_name='+new-recipe'), user=self.chef)
+ self.checkRelatedBranches(
+ set(), related_package_branch_info, browser.contents)
+
+
+class TestSourcePackageRecipeAddViewGit(
+ TestSourcePackageRecipeAddViewMixin, GitMixin, TestCaseForRecipe):
+
+ def makeBranchAndPackage(self):
+ product = self.factory.makeProduct(
+ name='ratatouille', displayname='Ratatouille')
+ repository = self.factory.makeGitRepository(
+ owner=self.chef, target=product, name=u'veggies')
+ self.factory.makeDistributionSourcePackage(
+ sourcepackagename='ratatouille')
+ return repository
+
+
class TestSourcePackageRecipeEditViewMixin:
"""Test the editing behaviour of a source package recipe."""
=== modified file 'lib/lp/code/browser/tests/test_tales.py'
--- lib/lp/code/browser/tests/test_tales.py 2015-09-11 06:04:36 +0000
+++ lib/lp/code/browser/tests/test_tales.py 2016-01-12 15:31:39 +0000
@@ -235,7 +235,7 @@
'<a href="%s">%s recipe build in ubuntu shiny</a> '
'[~eric/ubuntu/ppa]'
% (canonical_url(build, path_only_if_possible=True),
- build.recipe.base_branch.unique_name)))
+ build.recipe.base.unique_name)))
def test_link_no_recipe(self):
eric = self.factory.makePerson(name='eric')
=== modified file 'lib/lp/code/help/related-recipes.html'
--- lib/lp/code/help/related-recipes.html 2010-11-04 22:50:52 +0000
+++ lib/lp/code/help/related-recipes.html 2016-01-12 15:31:39 +0000
@@ -18,8 +18,8 @@
</p>
<p>A "recipe" is a description of the steps Launchpad's package builder
- should take to construct a source package from a set of Bazaar branches
- that you specify.
+ should take to construct a source package from a set of Bazaar or Git
+ branches that you specify.
</p>
<p>
=== modified file 'lib/lp/code/templates/gitref-recipes.pt'
--- lib/lp/code/templates/gitref-recipes.pt 2016-01-12 15:31:39 +0000
+++ lib/lp/code/templates/gitref-recipes.pt 2016-01-12 15:31:39 +0000
@@ -2,6 +2,7 @@
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ tal:define="context_menu view/context/menu:context"
id="related-recipes">
<h3>Related source package recipes</h3>
@@ -14,6 +15,12 @@
<a href="/+help-code/related-recipes.html" target="help"
class="sprite maybe action-icon">(?)</a>
</div>
+
+ <span
+ tal:define="link context_menu/create_recipe"
+ tal:condition="link/enabled"
+ tal:replace="structure link/render"
+ />
</div>
</div>
=== modified file 'lib/lp/code/templates/gitrepository-recipes.pt'
--- lib/lp/code/templates/gitrepository-recipes.pt 2016-01-12 15:31:39 +0000
+++ lib/lp/code/templates/gitrepository-recipes.pt 2016-01-12 15:31:39 +0000
@@ -2,6 +2,7 @@
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ tal:define="context_menu view/context/menu:context"
id="related-recipes">
<h3>Related source package recipes</h3>
@@ -14,6 +15,12 @@
<a href="/+help-code/related-recipes.html" target="help"
class="sprite maybe action-icon">(?)</a>
</div>
+
+ <span
+ tal:define="link context_menu/create_recipe"
+ tal:condition="link/enabled"
+ tal:replace="structure link/render"
+ />
</div>
</div>
Follow ups