launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #19865
[Merge] lp:~cjwatson/launchpad/git-recipe-browser-existing into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/git-recipe-browser-existing into lp:launchpad with lp:~cjwatson/launchpad/git-recipe-stale as a prerequisite.
Commit message:
Allow editing existing 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-existing/+merge/282297
Allow editing existing Git recipes.
I plan to put the browser code to create new recipes in place more or less last; adding browser code for existing recipes gives me the opportunity to put the bulk of the test code in place.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-recipe-browser-existing into lp:launchpad.
=== modified file 'lib/lp/app/widgets/suggestion.py'
--- lib/lp/app/widgets/suggestion.py 2015-10-26 14:54:43 +0000
+++ lib/lp/app/widgets/suggestion.py 2016-01-12 12:32:25 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Widgets related to IBranch."""
@@ -355,7 +355,7 @@
class RecipeOwnerWidget(SuggestionWidget):
"""Widget for selecting a recipe owner.
- The current user and the base branch owner are suggested.
+ The current user and the base source owner are suggested.
"""
@staticmethod
=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
--- lib/lp/code/browser/sourcepackagerecipe.py 2016-01-11 21:11:27 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py 2016-01-12 12:32:25 +0000
@@ -86,9 +86,12 @@
from lp.code.errors import (
BuildAlreadyPending,
NoSuchBranch,
+ NoSuchGitRepository,
PrivateBranchRecipe,
+ PrivateGitRepositoryRecipe,
TooNewRecipeFormat,
)
+from lp.code.interfaces.branch import IBranch
from lp.code.interfaces.branchtarget import IBranchTarget
from lp.code.interfaces.sourcepackagerecipe import (
IRecipeBranchSource,
@@ -627,12 +630,17 @@
except ForbiddenInstructionError as e:
self.setFieldError(
'recipe_text',
- 'The bzr-builder instruction "%s" is not permitted '
- 'here.' % e.instruction_name)
+ 'The recipe instruction "%s" is not permitted here.' %
+ e.instruction_name)
except NoSuchBranch as e:
self.setFieldError(
'recipe_text', '%s is not a branch on Launchpad.' % e.name)
- except (RecipeParseError, PrivateBranchRecipe) as e:
+ except NoSuchGitRepository as e:
+ self.setFieldError(
+ 'recipe_text',
+ '%s is not a Git repository on Launchpad.' % e.name)
+ except (RecipeParseError, PrivateBranchRecipe,
+ PrivateGitRepositoryRecipe) as e:
self.setFieldError('recipe_text', str(e))
raise ErrorHandled()
@@ -676,24 +684,24 @@
super(RecipeRelatedBranchesMixin, self).setUpWidgets(context)
self.widgets['related-branches'].display_label = False
self.widgets['related-branches'].setRenderedValue(dict(
- related_package_branch_info=self.related_package_branch_info,
- related_series_branch_info=self.related_series_branch_info))
+ related_package_branch_info=self.related_package_branch_info,
+ related_series_branch_info=self.related_series_branch_info))
@cachedproperty
def related_series_branch_info(self):
branch_to_check = self.getBranch()
- return IBranchTarget(
- branch_to_check.target).getRelatedSeriesBranchInfo(
- branch_to_check,
- limit_results=5)
+ if IBranch.providedBy(branch_to_check):
+ branch_target = IBranchTarget(branch_to_check.target)
+ return branch_target.getRelatedSeriesBranchInfo(
+ branch_to_check, limit_results=5)
@cachedproperty
def related_package_branch_info(self):
branch_to_check = self.getBranch()
- return IBranchTarget(
- branch_to_check.target).getRelatedPackageBranchInfo(
- branch_to_check,
- limit_results=5)
+ if IBranch.providedBy(branch_to_check):
+ branch_target = IBranchTarget(branch_to_check.target)
+ return branch_target.getRelatedPackageBranchInfo(
+ branch_to_check, limit_results=5)
class SourcePackageRecipeAddView(RecipeRelatedBranchesMixin,
@@ -822,8 +830,8 @@
"""View for editing Source Package Recipes."""
def getBranch(self):
- """The branch on which the recipe is built."""
- return self.context.base_branch
+ """The branch or repository which the recipe is built."""
+ return self.context.base
@property
def title(self):
@@ -847,7 +855,7 @@
any_owner_choice = PersonChoice(
__name__='owner', title=owner_field.title,
description=(u"As an administrator you are able to assign"
- u" this branch to any person or team."),
+ u" this recipe to any person or team."),
required=True, vocabulary='ValidPersonOrTeam')
any_owner_field = form.Fields(
any_owner_choice, render_context=self.render_context)
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2016-01-11 21:11:27 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2016-01-12 12:32:25 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010-2013 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Tests for the source package recipe view classes and templates."""
@@ -10,6 +10,7 @@
datetime,
timedelta,
)
+import re
from textwrap import dedent
from BeautifulSoup import BeautifulSoup
@@ -34,7 +35,10 @@
from lp.code.browser.sourcepackagerecipebuild import (
SourcePackageRecipeBuildView,
)
-from lp.code.interfaces.sourcepackagerecipe import MINIMAL_RECIPE_TEXT_BZR
+from lp.code.interfaces.sourcepackagerecipe import (
+ MINIMAL_RECIPE_TEXT_BZR,
+ MINIMAL_RECIPE_TEXT_GIT,
+ )
from lp.code.tests.helpers import recipe_parser_newest_version
from lp.registry.interfaces.person import TeamMembershipPolicy
from lp.registry.interfaces.pocket import PackagePublishingPocket
@@ -89,35 +93,21 @@
canonical_url(recipe))
-class TestCaseForRecipe(BrowserTestCase):
- """Create some sample data for recipe tests."""
-
- def setUp(self):
- """Provide useful defaults."""
- super(TestCaseForRecipe, self).setUp()
- self.chef = self.factory.makePerson(
- displayname='Master Chef', name='chef')
- self.user = self.chef
- self.ppa = self.factory.makeArchive(
- displayname='Secret PPA', owner=self.chef, name='ppa')
- self.squirrel = self.factory.makeDistroSeries(
- displayname='Secret Squirrel', name='secret', version='100.04',
- distribution=self.ppa.distribution)
- naked_squirrel = removeSecurityProxy(self.squirrel)
- naked_squirrel.nominatedarchindep = self.squirrel.newArch(
- 'i386', getUtility(IProcessorSet).getByName('386'), False,
- self.chef)
-
- def makeRecipe(self):
- """Create and return a specific recipe."""
- chocolate = self.factory.makeProduct(name='chocolate')
- cake_branch = self.factory.makeProductBranch(
- owner=self.chef, name='cake', product=chocolate)
- return self.factory.makeSourcePackageRecipe(
- owner=self.chef, distroseries=self.squirrel, name=u'cake_recipe',
- description=u'This recipe builds a foo for disto bar, with my'
- ' Secret Squirrel changes.', branches=[cake_branch],
- daily_build_archive=self.ppa)
+class BzrMixin:
+ """Mixin for Bazaar-based recipe tests."""
+
+ minimal_recipe_text = MINIMAL_RECIPE_TEXT_BZR
+ branch_type = "branch"
+ no_such_object_message = "is not a branch on Launchpad."
+
+ def makeBranch(self, target=None, **kwargs):
+ return self.factory.makeAnyBranch(product=target, **kwargs)
+
+ def makePackageBranch(self, **kwargs):
+ return self.factory.makePackageBranch(**kwargs)
+
+ def makeRelatedBranches(self, *args, **kwargs):
+ return self.factory.makeRelatedBranches(*args, **kwargs)
def checkRelatedBranches(self, related_series_branch_info,
related_package_branch_info, browser_contents):
@@ -194,6 +184,86 @@
expected_branch_info.append(product_series.name)
self.assertEqual(expected_branch_info, series_branches_info)
+ @staticmethod
+ def getRepository(branch):
+ return branch
+
+ @staticmethod
+ def getBranchRecipeText(branch):
+ return branch.identity
+
+ @staticmethod
+ def getMinimalRecipeText(branch):
+ return MINIMAL_RECIPE_TEXT_BZR % branch.identity
+
+
+class GitMixin:
+ """Mixin for Git-based recipe tests."""
+
+ minimal_recipe_text = MINIMAL_RECIPE_TEXT_GIT
+ branch_type = "repository"
+ no_such_object_message = "is not a Git repository on Launchpad."
+
+ def makeBranch(self, **kwargs):
+ return self.factory.makeGitRefs(**kwargs)[0]
+
+ def makePackageBranch(self, sourcepackagename=None, **kwargs):
+ dsp = self.factory.makeDistributionSourcePackage(
+ sourcepackagename=sourcepackagename)
+ return self.factory.makeGitRefs(target=dsp, **kwargs)[0]
+
+ def makeRelatedBranches(self, reference_branch=None, *args, **kwargs):
+ if reference_branch is None:
+ [reference_branch] = self.factory.makeGitRefs()
+ return reference_branch, [], []
+
+ def checkRelatedBranches(self, *args, **kwargs):
+ pass
+
+ @staticmethod
+ def getRepository(branch):
+ return branch.repository
+
+ @staticmethod
+ def getBranchRecipeText(branch):
+ return "%s %s" % (branch.repository.identity, branch.name)
+
+ @staticmethod
+ def getMinimalRecipeText(branch):
+ return MINIMAL_RECIPE_TEXT_GIT % (
+ branch.repository.identity, branch.name)
+
+
+class TestCaseForRecipe(BrowserTestCase):
+ """Create some sample data for recipe tests."""
+
+ def setUp(self):
+ """Provide useful defaults."""
+ super(TestCaseForRecipe, self).setUp()
+ self.chef = self.factory.makePerson(
+ displayname='Master Chef', name='chef')
+ self.user = self.chef
+ self.ppa = self.factory.makeArchive(
+ displayname='Secret PPA', owner=self.chef, name='ppa')
+ self.squirrel = self.factory.makeDistroSeries(
+ displayname='Secret Squirrel', name='secret', version='100.04',
+ distribution=self.ppa.distribution)
+ naked_squirrel = removeSecurityProxy(self.squirrel)
+ naked_squirrel.nominatedarchindep = self.squirrel.newArch(
+ 'i386', getUtility(IProcessorSet).getByName('386'), False,
+ self.chef)
+
+ def makeRecipe(self, **kwargs):
+ """Create and return a specific recipe."""
+ chocolate = self.factory.makeProduct(name='chocolate')
+ cake_branch = self.makeBranch(
+ owner=self.chef, name=u'cake', target=chocolate)
+ return self.factory.makeSourcePackageRecipe(
+ owner=self.chef, distroseries=self.squirrel, name=u'cake_recipe',
+ description=u'This recipe builds a foo for distro bar, with my'
+ ' Secret Squirrel changes.', branches=[cake_branch],
+ daily_build_archive=self.ppa, **kwargs)
+
class TestSourcePackageRecipeAddViewInitalValues(TestCaseWithFactory):
@@ -272,7 +342,7 @@
self.assertEqual(set(), series.intersection(other_series))
-class TestSourcePackageRecipeAddView(TestCaseForRecipe):
+class TestSourcePackageRecipeAddView(BzrMixin, TestCaseForRecipe):
layer = DatabaseFunctionalLayer
@@ -399,8 +469,7 @@
browser.getControl('Create Recipe').click()
self.assertEqual(
get_feedback_messages(browser.contents)[1],
- html_escape(
- 'The bzr-builder instruction "run" is not permitted here.'))
+ html_escape('The recipe instruction "run" is not permitted here.'))
def createRecipe(self, recipe_text, branch=None):
if branch is None:
@@ -730,7 +799,7 @@
self.assertIsNot(None, new_ppa)
-class TestSourcePackageRecipeEditView(TestCaseForRecipe):
+class TestSourcePackageRecipeEditViewMixin:
"""Test the editing behaviour of a source package recipe."""
layer = DatabaseFunctionalLayer
@@ -741,10 +810,10 @@
distribution=self.ppa.distribution)
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- veggie_branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
- meat_branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='meat')
+ veggie_branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
+ meat_branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'meat')
recipe = self.factory.makeSourcePackageRecipe(
owner=self.chef, registrant=self.chef,
name=u'things', description=u'This is a recipe',
@@ -754,14 +823,13 @@
distribution=self.ppa.distribution, name='ppa2',
displayname="PPA 2", owner=self.chef)
- meat_path = meat_branch.bzr_identity
+ recipe_text = self.getMinimalRecipeText(meat_branch)
browser = self.getUserBrowser(canonical_url(recipe), user=self.chef)
browser.getLink('Edit recipe').click()
browser.getControl(name='field.name').value = 'fings'
browser.getControl('Description').value = 'This is stuff'
- browser.getControl('Recipe text').value = (
- MINIMAL_RECIPE_TEXT_BZR % meat_path)
+ browser.getControl('Recipe text').value = recipe_text
browser.getControl('Secret Squirrel').click()
browser.getControl('Mumbly Midget').click()
browser.getControl('PPA 2').click()
@@ -771,10 +839,7 @@
self.assertThat(
'Edit This is stuff', MatchesTagText(content, 'edit-description'))
self.assertThat(
- 'Edit '
- '# bzr-builder format 0.3 deb-version {debupstream}-0~{revno}\n'
- 'lp://dev/~chef/ratatouille/meat',
- MatchesTagText(content, 'edit-recipe_text'))
+ 'Edit ' + recipe_text, MatchesTagText(content, 'edit-recipe_text'))
self.assertThat(
'Distribution series: Edit Mumbly Midget',
MatchesTagText(content, 'distroseries'))
@@ -784,8 +849,7 @@
def test_edit_recipe_sets_date_last_modified(self):
"""Editing a recipe sets the date_last_modified property."""
date_created = datetime(2000, 1, 1, 12, tzinfo=UTC)
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, date_created=date_created)
+ recipe = self.makeRecipe(date_created=date_created)
login_person(self.chef)
view = SourcePackageRecipeEditView(recipe, LaunchpadTestRequest())
@@ -803,17 +867,17 @@
distribution=self.ppa.distribution)
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- veggie_branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
- meat_branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='meat')
+ veggie_branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
+ meat_branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'meat')
recipe = self.factory.makeSourcePackageRecipe(
owner=self.chef, registrant=self.chef,
name=u'things', description=u'This is a recipe',
distroseries=self.squirrel, branches=[veggie_branch],
daily_build_archive=self.ppa)
- meat_path = meat_branch.bzr_identity
+ recipe_text = self.getMinimalRecipeText(meat_branch)
expert = getUtility(ILaunchpadCelebrities).admin.teamowner
browser = self.getUserBrowser(canonical_url(recipe), user=expert)
@@ -827,8 +891,7 @@
browser.getControl(name='field.name').value = 'fings'
browser.getControl('Description').value = 'This is stuff'
- browser.getControl('Recipe text').value = (
- MINIMAL_RECIPE_TEXT_BZR % meat_path)
+ browser.getControl('Recipe text').value = recipe_text
browser.getControl('Secret Squirrel').click()
browser.getControl('Mumbly Midget').click()
browser.getControl('Update Recipe').click()
@@ -838,10 +901,7 @@
self.assertThat(
'Edit This is stuff', MatchesTagText(content, 'edit-description'))
self.assertThat(
- 'Edit '
- '# bzr-builder format 0.3 deb-version {debupstream}-0~{revno}\n'
- 'lp://dev/~chef/ratatouille/meat',
- MatchesTagText(content, 'edit-recipe_text'))
+ 'Edit ' + recipe_text, MatchesTagText(content, 'edit-recipe_text'))
self.assertThat(
'Distribution series: Edit Mumbly Midget',
MatchesTagText(content, 'distroseries'))
@@ -852,8 +912,8 @@
distribution=self.ppa.distribution)
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- veggie_branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
+ veggie_branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
recipe = self.factory.makeSourcePackageRecipe(
owner=self.chef, registrant=self.chef,
name=u'things', description=u'This is a recipe',
@@ -867,8 +927,7 @@
self.assertEqual(
get_feedback_messages(browser.contents)[1],
- html_escape(
- 'The bzr-builder instruction "run" is not permitted here.'))
+ html_escape('The recipe instruction "run" is not permitted here.'))
def test_edit_recipe_format_too_new(self):
# If the recipe's format version is too new, we should notify the
@@ -878,17 +937,16 @@
distribution=self.ppa.distribution)
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- veggie_branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
+ veggie_branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
recipe = self.factory.makeSourcePackageRecipe(
owner=self.chef, registrant=self.chef,
name=u'things', description=u'This is a recipe',
distroseries=self.squirrel, branches=[veggie_branch])
- new_recipe_text = dedent(u'''\
- # bzr-builder format 145.115 deb-version {debupstream}-0~{revno}
- %s
- ''') % recipe.base_branch.bzr_identity
+ new_recipe_text = re.sub(
+ 'format [^ ]*', 'format 145.115',
+ self.getMinimalRecipeText(veggie_branch))
with recipe_parser_newest_version(145.115):
browser = self.getViewBrowser(recipe)
@@ -906,10 +964,10 @@
distribution=self.ppa.distribution)
product = self.factory.makeProduct(
name='ratatouille', displayname='Ratatouille')
- veggie_branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='veggies')
- meat_branch = self.factory.makeBranch(
- owner=self.chef, product=product, name='meat')
+ veggie_branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'veggies')
+ meat_branch = self.makeBranch(
+ owner=self.chef, target=product, name=u'meat')
recipe = self.factory.makeSourcePackageRecipe(
owner=self.chef, registrant=self.chef,
name=u'things', description=u'This is a recipe',
@@ -919,14 +977,13 @@
name=u'fings', description=u'This is a recipe',
distroseries=self.squirrel, branches=[veggie_branch])
- meat_path = meat_branch.bzr_identity
+ recipe_text = self.getMinimalRecipeText(meat_branch)
browser = self.getUserBrowser(canonical_url(recipe), user=self.chef)
browser.getLink('Edit recipe').click()
browser.getControl(name='field.name').value = 'fings'
browser.getControl('Description').value = 'This is stuff'
- browser.getControl('Recipe text').value = (
- MINIMAL_RECIPE_TEXT_BZR % meat_path)
+ browser.getControl('Recipe text').value = recipe_text
browser.getControl('Secret Squirrel').click()
browser.getControl('Mumbly Midget').click()
browser.getControl('Update Recipe').click()
@@ -939,30 +996,32 @@
# If a user tries to set source package recipe to use a private
# branch, they should get an error.
recipe = self.factory.makeSourcePackageRecipe(owner=self.user)
- 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.getViewBrowser(recipe, '+edit')
browser.getControl('Recipe text').value = recipe_text
browser.getControl('Update Recipe').click()
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_edit_recipe_no_branch(self):
# If a user tries to set a source package recipe to use a branch
- # that isn't registred, they will get an error.
- recipe = self.factory.makeSourcePackageRecipe(owner=self.user)
- no_branch_recipe_text = recipe.recipe_text[:-4]
- expected_name = recipe.base_branch.unique_name[:-3]
+ # that isn't registered, they will get an error.
+ recipe = self.factory.makeSourcePackageRecipe(
+ owner=self.user, branches=[self.makeBranch()])
+ no_branch_recipe_text = (
+ recipe.recipe_text.splitlines()[0] + "\nlp:nonexistent\n")
browser = self.getViewBrowser(recipe, '+edit')
browser.getControl('Recipe text').value = no_branch_recipe_text
browser.getControl('Update Recipe').click()
self.assertEqual(
get_feedback_messages(browser.contents)[1],
- 'lp://dev/%s is not a branch on Launchpad.' % expected_name)
+ 'lp:nonexistent %s' % self.no_such_object_message)
def _test_edit_recipe_with_no_related_branches(self, recipe):
# The Related Branches section should not appear if there are no
@@ -978,26 +1037,31 @@
def test_edit_product_branch_with_no_related_branches_recipe(self):
# The Related Branches section should not appear if there are no
# related branches.
- base_branch = self.factory.makeBranch()
+ base_branch = self.makeBranch()
recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, branches=[base_branch])
+ owner=self.chef, branches=[base_branch])
self._test_edit_recipe_with_no_related_branches(recipe)
def test_edit_sourcepackage_branch_with_no_related_branches_recipe(self):
# The Related Branches section should not appear if there are no
# related branches.
- base_branch = self.factory.makePackageBranch()
+ base_branch = self.makePackageBranch()
recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, branches=[base_branch])
+ owner=self.chef, branches=[base_branch])
self._test_edit_recipe_with_no_related_branches(recipe)
+
+class TestSourcePackageRecipeEditViewBzr(
+ TestSourcePackageRecipeEditViewMixin, BzrMixin, TestCaseForRecipe):
+
def test_edit_recipe_with_package_branches(self):
# The series branches table should not appear if there are none.
with person_logged_in(self.chef):
- recipe = self.factory.makeSourcePackageRecipe(owner=self.chef)
- self.factory.makeRelatedBranches(
- reference_branch=recipe.base_branch,
- with_series_branches=False)
+ base_branch = self.makeBranch()
+ recipe = self.factory.makeSourcePackageRecipe(
+ owner=self.chef, branches=[base_branch])
+ self.makeRelatedBranches(
+ reference_branch=base_branch, with_series_branches=False)
browser = self.getUserBrowser(canonical_url(recipe), user=self.chef)
browser.getLink('Edit recipe').click()
soup = BeautifulSoup(browser.contents)
@@ -1013,10 +1077,11 @@
def test_edit_recipe_with_series_branches(self):
# The package branches table should not appear if there are none.
with person_logged_in(self.chef):
- recipe = self.factory.makeSourcePackageRecipe(owner=self.chef)
- self.factory.makeRelatedBranches(
- reference_branch=recipe.base_branch,
- with_package_branches=False)
+ base_branch = self.makeBranch()
+ recipe = self.factory.makeSourcePackageRecipe(
+ owner=self.chef, branches=[base_branch])
+ self.makeRelatedBranches(
+ reference_branch=base_branch, with_package_branches=False)
browser = self.getUserBrowser(canonical_url(recipe), user=self.chef)
browser.getLink('Edit recipe').click()
soup = BeautifulSoup(browser.contents)
@@ -1032,11 +1097,11 @@
def test_edit_product_branch_recipe_with_related_branches(self):
# The related branches should be rendered correctly on the page.
with person_logged_in(self.chef):
- recipe = self.factory.makeSourcePackageRecipe(owner=self.chef)
- (branch, related_series_branch_info,
- related_package_branch_info) = (
- self.factory.makeRelatedBranches(
- reference_branch=recipe.base_branch))
+ base_branch = self.makeBranch()
+ recipe = self.factory.makeSourcePackageRecipe(
+ owner=self.chef, branches=[base_branch])
+ _, related_series_branch_info, related_package_branch_info = (
+ self.makeRelatedBranches(reference_branch=base_branch))
browser = self.getUserBrowser(
canonical_url(recipe, view_name='+edit'), user=self.chef)
self.checkRelatedBranches(
@@ -1046,18 +1111,23 @@
def test_edit_sourcepackage_branch_recipe_with_related_branches(self):
# The related branches should be rendered correctly on the page.
with person_logged_in(self.chef):
- reference_branch = self.factory.makePackageBranch()
+ reference_branch = self.makePackageBranch()
recipe = self.factory.makeSourcePackageRecipe(
owner=self.chef, branches=[reference_branch])
- (branch, ignore, related_package_branch_info) = (
- self.factory.makeRelatedBranches(reference_branch))
- browser = self.getUserBrowser(
- canonical_url(recipe, view_name='+edit'), user=self.chef)
- self.checkRelatedBranches(
- set(), related_package_branch_info, browser.contents)
-
-
-class TestSourcePackageRecipeView(TestCaseForRecipe):
+ _, _, related_package_branch_info = (
+ self.makeRelatedBranches(reference_branch))
+ browser = self.getUserBrowser(
+ canonical_url(recipe, view_name='+edit'), user=self.chef)
+ self.checkRelatedBranches(
+ set(), related_package_branch_info, browser.contents)
+
+
+class TestSourcePackageRecipeEditViewGit(
+ TestSourcePackageRecipeEditViewMixin, GitMixin, TestCaseForRecipe):
+ pass
+
+
+class TestSourcePackageRecipeViewMixin:
layer = LaunchpadFunctionalLayer
@@ -1086,8 +1156,8 @@
Recipe information
Build schedule: .* Built on request Edit
Owner: Master Chef Edit
- Base branch: lp://dev/~chef/chocolate/cake
- Debian version: {debupstream}-0~{revno}
+ Base source: lp:.*~chef/chocolate.*cake
+ Debian version: {debupstream}-0~{rev.*}
Daily build archive: Secret PPA Edit
Distribution series: Edit Secret Squirrel
@@ -1097,8 +1167,8 @@
Request build\(s\)
Recipe contents Edit
- # bzr-builder format 0.3 deb-version {debupstream}-0~{revno}
- lp://dev/~chef/chocolate/cake""", self.getMainText(build.recipe))
+ # .* format .* deb-version {debupstream}-0~{rev.*}
+ lp:.*~chef/chocolate.*cake.*""", self.getMainText(build.recipe))
def test_index_success_with_buildlog(self):
# The buildlog is shown if it is there.
@@ -1262,9 +1332,7 @@
[build6, build5, build4, build3, build2], view.builds)
def test_request_builds_redirects_on_get(self):
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=self.ppa,
- is_stale=True, build_daily=True)
+ recipe = self.makeRecipe(is_stale=True, build_daily=True)
with person_logged_in(self.chef):
url = canonical_url(recipe)
browser = self.getViewBrowser(recipe, '+request-daily-build')
@@ -1272,9 +1340,7 @@
def test_request_daily_builds_button_stale(self):
# Recipes that are stale and are built daily have a build now link
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=self.ppa,
- is_stale=True, build_daily=True)
+ recipe = self.makeRecipe(is_stale=True, build_daily=True)
browser = self.getViewBrowser(recipe)
build_button = find_tag_by_id(browser.contents, 'field.actions.build')
self.assertIsNot(None, build_button)
@@ -1282,9 +1348,7 @@
def test_request_daily_builds_button_not_stale(self):
# Recipes that are not stale do not have a build now link
login(ANONYMOUS)
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=self.ppa,
- is_stale=False, build_daily=True)
+ recipe = self.makeRecipe(is_stale=False, build_daily=True)
browser = self.getViewBrowser(recipe)
build_button = find_tag_by_id(browser.contents, 'field.actions.build')
self.assertIs(None, build_button)
@@ -1292,9 +1356,7 @@
def test_request_daily_builds_button_not_daily(self):
# Recipes that are not built daily do not have a build now link
login(ANONYMOUS)
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=self.ppa,
- is_stale=True, build_daily=False)
+ recipe = self.makeRecipe(is_stale=True, build_daily=False)
browser = self.getViewBrowser(recipe)
build_button = find_tag_by_id(browser.contents, 'field.actions.build')
self.assertIs(None, build_button)
@@ -1302,8 +1364,10 @@
def test_request_daily_builds_button_no_daily_ppa(self):
# Recipes that have no daily build ppa do not have a build now link
login(ANONYMOUS)
+ branch = self.makeBranch()
recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, is_stale=True, build_daily=True)
+ owner=self.chef, branches=[branch],
+ is_stale=True, build_daily=True)
naked_recipe = removeSecurityProxy(recipe)
naked_recipe.daily_build_archive = None
browser = self.getViewBrowser(recipe)
@@ -1314,8 +1378,7 @@
# Recipes do not have a build now link if the user does not have edit
# permission on the recipe.
login(ANONYMOUS)
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, is_stale=True, build_daily=True)
+ recipe = self.makeRecipe(is_stale=True, build_daily=True)
person = self.factory.makePerson()
browser = self.getViewBrowser(recipe, user=person)
build_button = find_tag_by_id(browser.contents, 'field.actions.build')
@@ -1327,10 +1390,12 @@
login(ANONYMOUS)
distroseries = self.factory.makeSourcePackageRecipeDistroseries()
person = self.factory.makePerson()
+ branch = self.makeBranch()
daily_build_archive = self.factory.makeArchive(
- distribution=distroseries.distribution, owner=person)
+ distribution=distroseries.distribution, owner=person)
recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=daily_build_archive,
+ owner=self.chef, branches=[branch],
+ daily_build_archive=daily_build_archive,
is_stale=True, build_daily=True)
browser = self.getViewBrowser(recipe)
build_button = find_tag_by_id(browser.contents, 'field.actions.build')
@@ -1340,12 +1405,14 @@
# Recipes whose daily build ppa is disabled do not have a build now
# link.
distroseries = self.factory.makeSourcePackageRecipeDistroseries()
+ branch = self.makeBranch()
daily_build_archive = self.factory.makeArchive(
- distribution=distroseries.distribution, owner=self.user)
+ distribution=distroseries.distribution, owner=self.user)
with person_logged_in(self.user):
daily_build_archive.disable()
recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=daily_build_archive,
+ owner=self.chef, branches=[branch],
+ daily_build_archive=daily_build_archive,
is_stale=True, build_daily=True)
browser = self.getViewBrowser(recipe)
build_button = find_tag_by_id(browser.contents, 'field.actions.build')
@@ -1353,17 +1420,16 @@
def test_request_daily_builds_ajax_link_not_rendered(self):
# The Build now link should not be rendered without javascript.
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=self.ppa,
- is_stale=True, build_daily=True)
+ recipe = self.makeRecipe(is_stale=True, build_daily=True)
browser = self.getViewBrowser(recipe)
build_link = find_tag_by_id(browser.contents, 'request-daily-builds')
self.assertIs(None, build_link)
def test_request_daily_builds_action(self):
# Daily builds should be triggered when requested.
+ branch = self.makeBranch()
recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=self.ppa,
+ owner=self.chef, branches=[branch], daily_build_archive=self.ppa,
is_stale=True, build_daily=True)
browser = self.getViewBrowser(recipe)
browser.getControl('Build now').click()
@@ -1380,9 +1446,7 @@
def test_request_daily_builds_disabled_archive(self):
# Requesting a daily build from a disabled archive is a user error.
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=self.ppa,
- name=u'julia', is_stale=True, build_daily=True)
+ recipe = self.makeRecipe(is_stale=True, build_daily=True)
harness = LaunchpadFormHarness(
recipe, SourcePackageRecipeRequestDailyBuildView)
with person_logged_in(self.ppa.owner):
@@ -1394,9 +1458,7 @@
def test_request_daily_builds_obsolete_series(self):
# Requesting a daily build with an obsolete series gives a warning.
- recipe = self.factory.makeSourcePackageRecipe(
- owner=self.chef, daily_build_archive=self.ppa,
- name=u'julia', is_stale=True, build_daily=True)
+ recipe = self.makeRecipe(is_stale=True, build_daily=True)
warty = self.factory.makeSourcePackageRecipeDistroseries()
hoary = self.factory.makeSourcePackageRecipeDistroseries(name='hoary')
with person_logged_in(self.chef):
@@ -1457,21 +1519,26 @@
Unauthorized, browser.getLink('Request build(s)').click)
def test_request_builds_archive_no_daily_build_archive(self):
- recipe = self.factory.makeSourcePackageRecipe()
+ branch = self.makeBranch()
+ recipe = self.factory.makeSourcePackageRecipe(branches=[branch])
view = SourcePackageRecipeRequestBuildsView(recipe, None)
self.assertIs(None, view.initial_values.get('archive'))
def test_request_builds_archive_daily_build_archive_unuploadable(self):
+ branch = self.makeBranch()
ppa = self.factory.makeArchive()
- recipe = self.factory.makeSourcePackageRecipe(daily_build_archive=ppa)
+ recipe = self.factory.makeSourcePackageRecipe(
+ branches=[branch], daily_build_archive=ppa)
with person_logged_in(self.chef):
view = SourcePackageRecipeRequestBuildsView(recipe, None)
self.assertIs(None, view.initial_values.get('archive'))
def test_request_builds_archive(self):
+ branch = self.makeBranch()
ppa = self.factory.makeArchive(
displayname='Secret PPA', owner=self.chef, name='ppa2')
- recipe = self.factory.makeSourcePackageRecipe(daily_build_archive=ppa)
+ recipe = self.factory.makeSourcePackageRecipe(
+ branches=[branch], daily_build_archive=ppa)
with person_logged_in(self.chef):
view = SourcePackageRecipeRequestBuildsView(recipe, None)
self.assertEqual(ppa, view.initial_values.get('archive'))
@@ -1506,11 +1573,12 @@
# that owns the PPA.
registrant = self.factory.makePerson()
owner_team = self.factory.makeTeam(members=[registrant], name='team1')
+ branch = self.makeBranch()
ppa_team = self.factory.makeTeam(members=[registrant], name='team2')
ppa = self.factory.makeArchive(owner=ppa_team, name='ppa')
return self.factory.makeSourcePackageRecipe(
- registrant=registrant, owner=owner_team, daily_build_archive=ppa,
- build_daily=True)
+ registrant=registrant, owner=owner_team, branches=[branch],
+ daily_build_archive=ppa, build_daily=True)
def test_owner_with_no_ppa_upload_permission(self):
# Daily build with upload issues are a problem.
@@ -1541,7 +1609,9 @@
# When a PPA is disabled, it is only viewable to the owner. This
# case is handled with the view not showing builds into a disabled
# archive, rather than giving an Unauthorized error to the user.
- recipe = self.factory.makeSourcePackageRecipe(build_daily=True)
+ branch = self.makeBranch()
+ recipe = self.factory.makeSourcePackageRecipe(
+ branches=[branch], build_daily=True)
recipe.requestBuild(
recipe.daily_build_archive, recipe.owner, self.squirrel,
PackagePublishingPocket.RELEASE)
@@ -1553,23 +1623,34 @@
extract_text(find_main_content(browser.contents)))
-class TestSourcePackageRecipeBuildView(BrowserTestCase):
+class TestSourcePackageRecipeViewBzr(
+ TestSourcePackageRecipeViewMixin, BzrMixin, TestCaseForRecipe):
+ pass
+
+
+class TestSourcePackageRecipeViewGit(
+ TestSourcePackageRecipeViewMixin, GitMixin, TestCaseForRecipe):
+ pass
+
+
+class TestSourcePackageRecipeBuildViewMixin:
"""Test behaviour of SourcePackageRecipeBuildView."""
layer = LaunchpadFunctionalLayer
def setUp(self):
"""Provide useful defaults."""
- super(TestSourcePackageRecipeBuildView, self).setUp()
+ super(TestSourcePackageRecipeBuildViewMixin, self).setUp()
self.user = self.factory.makePerson(
displayname='Owner', name='build-owner')
def makeBuild(self):
- """Make a build suitabe for testing."""
+ """Make a build suitable for testing."""
archive = self.factory.makeArchive(name='build',
owner=self.user)
+ branch = self.makeBranch()
recipe = self.factory.makeSourcePackageRecipe(
- owner=self.user, name=u'my-recipe')
+ owner=self.user, name=u'my-recipe', branches=[branch])
distro_series = self.factory.makeDistroSeries(
name='squirrel', distribution=archive.distribution)
removeSecurityProxy(distro_series).nominatedarchindep = (
@@ -1607,7 +1688,9 @@
It should be bq.date_started + estimated duration for jobs that have
started.
"""
- build = self.factory.makeSourcePackageRecipeBuild()
+ branch = self.makeBranch()
+ recipe = self.factory.makeSourcePackageRecipe(branches=[branch])
+ build = self.factory.makeSourcePackageRecipeBuild(recipe=recipe)
view = SourcePackageRecipeBuildView(build, None)
self.assertIs(None, view.eta)
queue_entry = removeSecurityProxy(build.queueBuild())
@@ -1750,12 +1833,24 @@
self.assertEqual(upload_log_url, link.url)
-class TestSourcePackageRecipeDeleteView(TestCaseForRecipe):
+class TestSourcePackageRecipeBuildViewBzr(
+ TestSourcePackageRecipeBuildViewMixin, BzrMixin, BrowserTestCase):
+ pass
+
+
+class TestSourcePackageRecipeBuildViewGit(
+ TestSourcePackageRecipeBuildViewMixin, GitMixin, BrowserTestCase):
+ pass
+
+
+class TestSourcePackageRecipeDeleteViewMixin:
layer = DatabaseFunctionalLayer
def test_delete_recipe(self):
- recipe = self.factory.makeSourcePackageRecipe(owner=self.chef)
+ branch = self.makeBranch()
+ recipe = self.factory.makeSourcePackageRecipe(
+ owner=self.chef, branches=[branch])
browser = self.getUserBrowser(
canonical_url(recipe), user=self.chef)
@@ -1768,7 +1863,9 @@
browser.url)
def test_delete_recipe_no_permissions(self):
- recipe = self.factory.makeSourcePackageRecipe(owner=self.chef)
+ branch = self.makeBranch()
+ recipe = self.factory.makeSourcePackageRecipe(
+ owner=self.chef, branches=[branch])
nopriv_person = self.factory.makePerson()
recipe_url = canonical_url(recipe)
@@ -1784,7 +1881,17 @@
self.getUserBrowser, recipe_url + '/+delete', user=nopriv_person)
-class TestBrokenExistingRecipes(BrowserTestCase):
+class TestSourcePackageRecipeDeleteViewBzr(
+ TestSourcePackageRecipeDeleteViewMixin, BzrMixin, TestCaseForRecipe):
+ pass
+
+
+class TestSourcePackageRecipeDeleteViewGit(
+ TestSourcePackageRecipeDeleteViewMixin, GitMixin, TestCaseForRecipe):
+ pass
+
+
+class TestBrokenExistingRecipesMixin:
"""Existing recipes broken by builder updates need to be editable.
This happened with a 0.2 -> 0.3 release where the nest command was no
@@ -1795,19 +1902,17 @@
layer = LaunchpadFunctionalLayer
- RECIPE_FIRST_LINE = (
- "# bzr-builder format 0.2 deb-version {debupstream}+{revno}")
-
def makeBrokenRecipe(self):
"""Make a valid recipe, then break it."""
product = self.factory.makeProduct()
- b1 = self.factory.makeProductBranch(product=product)
- b2 = self.factory.makeProductBranch(product=product)
+ b1 = self.makeBranch(target=product)
+ b2 = self.makeBranch(target=product)
recipe_text = dedent("""\
%s
%s
nest name %s foo
- """ % (self.RECIPE_FIRST_LINE, b1.bzr_identity, b2.bzr_identity))
+ """ % (self.RECIPE_FIRST_LINE, self.getBranchRecipeText(b1),
+ self.getRepository(b2).identity))
recipe = self.factory.makeSourcePackageRecipe(recipe=recipe_text)
naked_data = removeSecurityProxy(recipe)._recipe_data
nest_instruction = list(naked_data.instructions)[0]
@@ -1831,3 +1936,17 @@
recipe = self.makeBrokenRecipe()
main_text = self.getMainText(recipe, '+edit', user=recipe.owner)
self.assertRecipeInText(main_text)
+
+
+class TestBrokenExistingRecipesBzr(
+ TestBrokenExistingRecipesMixin, BzrMixin, BrowserTestCase):
+
+ RECIPE_FIRST_LINE = (
+ "# bzr-builder format 0.2 deb-version {debupstream}+{revno}")
+
+
+class TestBrokenExistingRecipesGit(
+ TestBrokenExistingRecipesMixin, GitMixin, BrowserTestCase):
+
+ RECIPE_FIRST_LINE = (
+ "# git-build-recipe format 0.4 deb-version {debupstream}+{revtime}")
=== modified file 'lib/lp/code/templates/sourcepackagerecipe-index.pt'
--- lib/lp/code/templates/sourcepackagerecipe-index.pt 2014-05-14 09:23:56 +0000
+++ lib/lp/code/templates/sourcepackagerecipe-index.pt 2016-01-12 12:32:25 +0000
@@ -108,9 +108,9 @@
<dt>Owner:</dt>
<dd tal:content="structure view/person_picker"/>
</dl>
- <dl id="base-branch">
- <dt>Base branch:</dt>
- <dd tal:content="structure context/base_branch/fmt:link" />
+ <dl id="base-source">
+ <dt>Base source:</dt>
+ <dd tal:content="structure context/base/fmt:link" />
</dl>
<dl id="debian-version">
<dt>Debian version:</dt>
@@ -151,8 +151,8 @@
Y.on('lp:context:deb_version_template:changed', function(e) {
Y.lp.deprecated.ui.update_field('#debian-version dd', e.new_value);
});
- Y.on('lp:context:base_branch_link:changed', function(e) {
- Y.lp.deprecated.ui.update_field('#base-branch dd', e.new_value_html);
+ Y.on('lp:context:base_source_link:changed', function(e) {
+ Y.lp.deprecated.ui.update_field('#base-source dd', e.new_value_html);
});
Y.on('load', function() {
Y.lp.code.requestbuild_overlay.hookUpDailyBuildsSchedule();
Follow ups