launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #07414
[Merge] lp:~abentley/launchpad/remove-create-merge-proposal-job into lp:launchpad
Aaron Bentley has proposed merging lp:~abentley/launchpad/remove-create-merge-proposal-job into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #989831 in Launchpad itself: "CreateMergeProposalJobs are dead code"
https://bugs.launchpad.net/launchpad/+bug/989831
For more details, see:
https://code.launchpad.net/~abentley/launchpad/remove-create-merge-proposal-job/+merge/103929
= Summary =
Fix bug #989831: CreateMergeProposalJobs are dead code
== Proposed fix ==
Remove CreateMergeProposalJobs
== Pre-implementation notes ==
Discussed with deryck. We determined that this feature has not been used at all in 2012, and less than 100 times in 2011.
== LOC Rationale ==
Reduces LOC
== Implementation details ==
Deletion. Lots and lots of deletion. (And mostly my own hard work, too!)
== Tests ==
bin/test -v test_codehandler
== Demo and Q/A ==
Send an email to merge@code.*.launchpad.net. You should get an email back saying merge directives are no longer supported.
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/code/configure.zcml
lib/lp/services/config/schema-lazr.conf
lib/lp/code/interfaces/branchmergeproposal.py
lib/lp/code/mail/codehandler.py
lib/lp/code/model/tests/test_branchmergeproposal.py
lib/lp/code/mail/tests/test_codehandler.py
lib/lp/services/messages/model/message.py
lib/lp/services/mail/errortemplates/mergedirectivenotsupported.txt
lib/lp/testing/factory.py
lib/lp/services/messages/interfaces/message.py
lib/lp/code/model/branchmergeproposaljob.py
configs/testrunner/launchpad-lazr.conf
lib/lp/services/messages/tests/test_message.py
./lib/lp/services/config/schema-lazr.conf
489: Line exceeds 80 characters.
1100: Line exceeds 80 characters.
1107: Line exceeds 80 characters.
1699: Line exceeds 80 characters.
./lib/lp/services/mail/errortemplates/mergedirectivenotsupported.txt
1: Line exceeds 80 characters.
3: Line exceeds 80 characters.
./configs/testrunner/launchpad-lazr.conf
126: Line exceeds 80 characters.
--
https://code.launchpad.net/~abentley/launchpad/remove-create-merge-proposal-job/+merge/103929
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/remove-create-merge-proposal-job into lp:launchpad.
=== modified file 'configs/testrunner/launchpad-lazr.conf'
--- configs/testrunner/launchpad-lazr.conf 2012-01-18 01:46:02 +0000
+++ configs/testrunner/launchpad-lazr.conf 2012-04-27 19:10:25 +0000
@@ -30,10 +30,6 @@
access_log: /tmp/test-codehosting-access.log
internal_branch_by_id_root: file:///var/tmp/bazaar.launchpad.dev/mirrors
-[create_merge_proposals]
-oops_prefix: TMPCJ
-error_dir: /var/tmp/codehosting.test
-
[database]
rw_main_master: dbname=launchpad_ftest host=localhost
rw_main_slave: dbname=launchpad_ftest host=localhost
=== removed file 'cronscripts/create_merge_proposals.py'
--- cronscripts/create_merge_proposals.py 2012-01-01 03:14:54 +0000
+++ cronscripts/create_merge_proposals.py 1970-01-01 00:00:00 +0000
@@ -1,40 +0,0 @@
-#!/usr/bin/python -S
-#
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-# pylint: disable-msg=W0403
-
-"""Create BranchMergeProposals from email."""
-
-__metaclass__ = type
-
-import _pythonpath
-
-from zope.component import getUtility
-
-from lp.code.interfaces.branchmergeproposal import (
- ICreateMergeProposalJobSource,
- )
-from lp.services.config import config
-from lp.services.job.runner import JobRunner
-from lp.services.scripts.base import LaunchpadCronScript
-from lp.services.webapp.errorlog import globalErrorUtility
-
-
-class RunCreateMergeProposalJobs(LaunchpadCronScript):
- """Run create merge proposal jobs."""
-
- def main(self):
- globalErrorUtility.configure('create_merge_proposals')
- job_source = getUtility(ICreateMergeProposalJobSource)
- runner = JobRunner.fromReady(job_source, self.logger)
- runner.runAll()
- self.logger.info(
- 'Ran %d CreateMergeProposalJobs.' % len(runner.completed_jobs))
-
-
-if __name__ == '__main__':
- script = RunCreateMergeProposalJobs(
- 'create_merge_proposals', config.create_merge_proposals.dbuser)
- script.lock_and_run()
=== modified file 'lib/lp/code/configure.zcml'
--- lib/lp/code/configure.zcml 2012-04-24 06:00:11 +0000
+++ lib/lp/code/configure.zcml 2012-04-27 19:10:25 +0000
@@ -289,16 +289,6 @@
<!-- Branch Merge Proposal Jobs -->
- <class class="lp.code.model.branchmergeproposaljob.CreateMergeProposalJob">
- <allow interface="lp.services.messages.interfaces.message.IMessageJob"/>
- <allow interface="lp.code.interfaces.branchmergeproposal.ICreateMergeProposalJob"/>
- </class>
- <securedutility
- component="lp.code.model.branchmergeproposaljob.CreateMergeProposalJob"
- provides="lp.code.interfaces.branchmergeproposal.ICreateMergeProposalJobSource">
- <allow interface="lp.code.interfaces.branchmergeproposal.ICreateMergeProposalJobSource"/>
- </securedutility>
-
<class class="lp.code.model.branchmergeproposaljob.MergeProposalNeedsReviewEmailJob">
<allow interface="lp.code.interfaces.branchmergeproposal.IBranchMergeProposalJob"/>
<allow interface="lp.code.interfaces.branchmergeproposal.IMergeProposalNeedsReviewEmailJob"/>
=== modified file 'lib/lp/code/interfaces/branchmergeproposal.py'
--- lib/lp/code/interfaces/branchmergeproposal.py 2011-12-30 06:14:56 +0000
+++ lib/lp/code/interfaces/branchmergeproposal.py 2012-04-27 19:10:25 +0000
@@ -15,8 +15,6 @@
'IBranchMergeProposalListingBatchNavigator',
'ICodeReviewCommentEmailJob',
'ICodeReviewCommentEmailJobSource',
- 'ICreateMergeProposalJob',
- 'ICreateMergeProposalJobSource',
'IGenerateIncrementalDiffJob',
'IGenerateIncrementalDiffJobSource',
'IMergeProposalNeedsReviewEmailJob',
@@ -641,20 +639,6 @@
IBranchMergeProposal[name].schema = IBranchMergeProposal
-class ICreateMergeProposalJob(IRunnableJob):
- """A Job that creates a branch merge proposal.
-
- It uses a Message, which must contain a merge directive.
- """
-
-
-class ICreateMergeProposalJobSource(IJobSource):
- """Acquire MergeProposalJobs."""
-
- def create(message_bytes):
- """Return a CreateMergeProposalJob for this message."""
-
-
class IMergeProposalNeedsReviewEmailJob(IRunnableJob):
"""Email about a merge proposal needing a review.."""
=== modified file 'lib/lp/code/mail/codehandler.py'
--- lib/lp/code/mail/codehandler.py 2012-01-01 02:58:52 +0000
+++ lib/lp/code/mail/codehandler.py 2012-04-27 19:10:25 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -8,44 +8,22 @@
import os
import re
-from bzrlib.branch import Branch
-from bzrlib.errors import (
- NotAMergeDirective,
- NotBranchError,
- NotStacked,
- )
-from bzrlib.merge_directive import MergeDirective
-from bzrlib.transport import get_transport
-from bzrlib.urlutils import join as urljoin
-from lazr.uri import URI
from sqlobject import SQLObjectNotFound
import transaction
from zope.component import getUtility
from zope.interface import implements
from zope.security.interfaces import Unauthorized
-from lp.code.bzr import get_branch_formats
from lp.code.enums import (
- BranchType,
CodeReviewVote,
)
from lp.code.errors import (
- BranchCreationException,
- BranchMergeProposalExists,
UserNotBranchReviewer,
)
-from lp.code.interfaces.branchlookup import IBranchLookup
from lp.code.interfaces.branchmergeproposal import (
IBranchMergeProposalGetter,
- ICreateMergeProposalJobSource,
- )
-from lp.code.interfaces.branchnamespace import (
- lookup_branch_namespace,
- split_unique_name,
- )
-from lp.code.interfaces.branchtarget import check_default_stacked_on
-from lp.codehosting.bzrutils import is_branch_stackable
-from lp.codehosting.vfs import get_lp_server
+ )
+from lp.services.config import config
from lp.services.mail.commands import (
EmailCommand,
EmailCommandCollection,
@@ -65,8 +43,6 @@
from lp.services.mail.notification import send_process_error_notification
from lp.services.mail.sendmail import simple_sendmail
from lp.services.messages.interfaces.message import IMessageSet
-from lp.services.webapp import urlparse
-from lp.services.webapp.errorlog import globalErrorUtility
from lp.services.webapp.interfaces import ILaunchBag
@@ -89,14 +65,6 @@
"""The user-supplied vote is not an acceptable value."""
-class NonLaunchpadTarget(Exception):
- """Target branch is not registered with Launchpad."""
-
-
-class MissingMergeDirective(Exception):
- """Emailed merge proposal lacks a merge directive"""
-
-
class CodeReviewEmailCommandExecutionContext:
"""Passed as the only parameter to each code review email command.
@@ -283,7 +251,10 @@
deferred to jobs to create BranchMergeProposals.
"""
if email_addr.startswith('merge@'):
- return self.createMergeProposalJob(mail, email_addr, file_alias)
+ body = get_error_message('mergedirectivenotsupported.txt')
+ simple_sendmail(
+ config.canonical.noreply_from_address, [mail.get('from')],
+ 'Merge directive not supported.', body)
else:
try:
return self.processComment(mail, email_addr, file_alias)
@@ -294,23 +265,6 @@
'Error Creating Merge Proposal', body)
return True
- def createMergeProposalJob(self, mail, email_addr, file_alias):
- """Check that the message is signed and create the job."""
- try:
- ensure_not_weakly_authenticated(
- mail, email_addr, 'not-signed-md.txt',
- 'key-not-registered-md.txt', error_templates)
- except IncomingEmailError, error:
- user = getUtility(ILaunchBag).user
- send_process_error_notification(
- str(user.preferredemail.email),
- 'Submit Request Failure',
- error.message, mail, error.failing_command)
- transaction.abort()
- else:
- getUtility(ICreateMergeProposalJobSource).create(file_alias)
- return True
-
def processCommands(self, context, commands):
"""Process the various merge proposal commands against the context."""
processing_errors = []
@@ -401,292 +355,3 @@
return getter.get(merge_proposal_id)
except SQLObjectNotFound:
raise NonExistantBranchMergeProposalAddress(email_addr)
-
- def _acquireBranchesForProposal(self, md, submitter):
- """Find or create DB Branches from a MergeDirective.
-
- If the target is not a Launchpad branch, NonLaunchpadTarget will be
- raised. If the source is not a Launchpad branch, a REMOTE branch will
- be created implicitly, with submitter as its owner/registrant.
-
- :param md: The `MergeDirective` to get branch URLs from.
- :param submitter: The `Person` who requested that the merge be
- performed.
- :return: source_branch, target_branch
- """
- mp_target = getUtility(IBranchLookup).getByUrl(md.target_branch)
- if mp_target is None:
- raise NonLaunchpadTarget()
- # If the target branch cannot be stacked upon, then don't try to stack
- # upon it or get revisions form it.
- if md.bundle is None or check_default_stacked_on(mp_target) is None:
- mp_source = self._getSourceNoBundle(
- md, mp_target, submitter)
- else:
- mp_source = self._getSourceWithBundle(
- md, mp_target, submitter)
- return mp_source, mp_target
-
- @staticmethod
- def _getNewBranchInfo(url, target_branch, submitter):
- """Return the namespace and basename for a branch.
-
- If an LP URL is provided, the namespace and basename will match the
- LP URL.
-
- Otherwise, the target is used to determine the namespace, and the base
- depends on what was supplied.
-
- If a URL is supplied, its base is used.
-
- If no URL is supplied, 'merge' is used as the base.
-
- :param url: The public URL of the source branch, if any.
- :param target_branch: The target branch.
- :param submitter: The person submitting the merge proposal.
- """
- if url is not None:
- url = url.rstrip('/')
- branches = getUtility(IBranchLookup)
- unique_name = branches.uriToUniqueName(URI(url))
- if unique_name is not None:
- namespace_name, base = split_unique_name(unique_name)
- return lookup_branch_namespace(namespace_name), base
- if url is None:
- basename = 'merge'
- else:
- basename = urlparse(url)[2].split('/')[-1]
- namespace = target_branch.target.getNamespace(submitter)
- return namespace, basename
-
- def _getNewBranch(self, branch_type, url, target, submitter):
- """Return a new database branch.
-
- :param branch_type: The type of branch to create.
- :param url: The public location of the branch to create.
- :param product: The product associated with the branch to create.
- :param submitter: The person who requested the merge.
- """
- namespace, basename = self._getNewBranchInfo(url, target, submitter)
- if branch_type == BranchType.REMOTE:
- db_url = url
- else:
- db_url = None
- return namespace.createBranchWithPrefix(
- branch_type, basename, submitter, url=db_url)
-
- def _getSourceNoBundle(self, md, target, submitter):
- """Get a source branch for a merge directive with no bundle."""
- source_db_branch = getUtility(IBranchLookup).getByUrl(
- md.source_branch)
- if source_db_branch is None:
- source_db_branch = self._getNewBranch(
- BranchType.REMOTE, md.source_branch, target, submitter)
- return source_db_branch
-
- def _getOrCreateDBBranch(self, md, db_target, submitter):
- """Return the source branch, creating a new branch if necessary."""
- db_source = None
- if md.source_branch is not None:
- db_source = getUtility(IBranchLookup).getByUrl(md.source_branch)
- if db_source is None:
- db_source = self._getNewBranch(
- BranchType.HOSTED, md.source_branch, db_target, submitter)
- # Commit the transaction to make sure the new source branch is
- # visible to the XMLRPC server which provides the virtual file
- # system information.
- transaction.commit()
- return db_source
-
- def _openSourceBzrBranch(self, source_url, target_url, stacked_url):
- """Open the source bzr branch, creating a new branch if necessary."""
- try:
- return Branch.open(source_url)
- except NotBranchError:
- bzr_target = Branch.open(target_url)
- transport = get_transport(
- source_url,
- possible_transports=[bzr_target.bzrdir.root_transport])
- bzrdir = bzr_target.bzrdir.sprout(
- transport.base, bzr_target.last_revision(),
- force_new_repo=True, stacked=True, create_tree_if_local=False,
- possible_transports=[transport], source_branch=bzr_target)
- bzr_branch = bzrdir.open_branch()
- # Set the stacked url to be the relative url for the target.
- bzr_branch.set_stacked_on_url(stacked_url)
- return bzr_branch
-
- def _getSourceWithBundle(self, md, db_target, submitter):
- """Get a source branch for a merge directive with a bundle."""
- db_source = self._getOrCreateDBBranch(md, db_target, submitter)
- # Make sure that the target branch is stackable so that we only
- # install the revisions unique to the source branch. If the target
- # branch is not stackable, return the existing branch or a new hosted
- # source branch - one that has *no* Bazaar data. Together these
- # prevent users from using Launchpad disk space at a rate that is
- # disproportionately greater than data uploaded.
- mirrored_bzr_target = db_target.getBzrBranch()
- if not is_branch_stackable(mirrored_bzr_target):
- return db_source
- assert db_source.branch_type == BranchType.HOSTED, (
- "Source branch is not hosted.")
-
- # Create the LP server as if the submitter was pushing a branch to LP.
- lp_server = get_lp_server(submitter.id)
- lp_server.start_server()
- try:
- source_url = urljoin(lp_server.get_url(), db_source.unique_name)
- target_url = urljoin(lp_server.get_url(), db_target.unique_name)
- stacked_url = '/' + db_target.unique_name
- bzr_source = self._openSourceBzrBranch(
- source_url, target_url, stacked_url)
- if is_branch_stackable(bzr_source):
- # Set the stacked on URL if not set.
- try:
- bzr_source.get_stacked_on_url()
- except NotStacked:
- # We don't currently support pulling in the revisions if
- # the source branch exists and isn't stacked.
- # XXX Tim Penhey 2010-07-27 bug 610292
- # We should fail here and return an oops email to the
- # user.
- return db_source
- self._pullRevisionsFromMergeDirectiveIntoSourceBranch(
- md, target_url, bzr_source)
- # Get the puller to pull the branch into the mirrored area.
- formats = get_branch_formats(bzr_source)
- db_source.branchChanged(
- stacked_url, bzr_source.last_revision(), *formats)
- return db_source
- finally:
- lp_server.stop_server()
-
- def _pullRevisionsFromMergeDirectiveIntoSourceBranch(self, md,
- target_url,
- bzr_branch):
- """Pull the revisions from the merge directive into the branch.
-
- :param md: The merge directive
- :param target_url: The URL of the branch that the merge directive is
- targetting using the user's LP transport.
- :param bzr_branch: The bazaar branch entity for the branch that the
- revisions from the merge directive are being pulled into.
- """
- # Tell the merge directive to use the user's LP transport URL to get
- # access to any needed but not supplied revisions.
- md.target_branch = target_url
- md.install_revisions(bzr_branch.repository)
- bzr_branch.lock_write()
- try:
- bzr_branch.pull(bzr_branch, stop_revision=md.revision_id,
- overwrite=True)
- finally:
- bzr_branch.unlock()
-
- def findMergeDirectiveAndComment(self, message):
- """Extract the comment and Merge Directive from a SignedMessage."""
- body = None
- md = None
- for part in message.walk():
- if part.is_multipart():
- continue
- payload = part.get_payload(decode=True)
- content_type = part.get('Content-type', 'text/plain').lower()
- if content_type.startswith('text/plain'):
- body = payload
- charset = part.get_param('charset')
- if charset is not None:
- body = body.decode(charset)
- try:
- md = MergeDirective.from_lines(payload.splitlines(True))
- except NotAMergeDirective:
- pass
- if None not in (body, md):
- return body, md
- else:
- raise MissingMergeDirective()
-
- def processMergeProposal(self, message):
- """Generate a merge proposal (and comment) from an email message.
-
- The message is expected to contain a merge directive in one of its
- parts. Its values are used to generate a BranchMergeProposal.
- If the message has a non-empty body, it is turned into a
- CodeReviewComment.
- """
- submitter = getUtility(ILaunchBag).user
- try:
- email_body_text, md = self.findMergeDirectiveAndComment(message)
- except MissingMergeDirective:
- body = get_error_message(
- 'missingmergedirective.txt',
- error_templates=error_templates)
- simple_sendmail('merge@xxxxxxxxxxxxxxxxxx',
- [message.get('from')],
- 'Error Creating Merge Proposal', body)
- return
- oops_message = (
- 'target: %r source: %r' %
- (md.target_branch, md.source_branch))
- with globalErrorUtility.oopsMessage(oops_message):
- try:
- source, target = self._acquireBranchesForProposal(
- md, submitter)
- except NonLaunchpadTarget:
- body = get_error_message('nonlaunchpadtarget.txt',
- error_templates=error_templates,
- target_branch=md.target_branch)
- simple_sendmail('merge@xxxxxxxxxxxxxxxxxx',
- [message.get('from')],
- 'Error Creating Merge Proposal', body)
- return
- except BranchCreationException, e:
- body = get_error_message(
- 'branch-creation-exception.txt',
- error_templates=error_templates,
- reason=e)
- simple_sendmail('merge@xxxxxxxxxxxxxxxxxx',
- [message.get('from')],
- 'Error Creating Merge Proposal', body)
- return
- with globalErrorUtility.oopsMessage(
- 'target: %r source: %r' % (target, source)):
- try:
- # When creating a merge proposal, we need to gather all
- # necessary arguments to addLandingTarget(). So from the email
- # body we need to extract: reviewer, review type, description.
- description = None
- review_requests = []
- email_body_text = email_body_text.strip()
- if email_body_text != '':
- description = email_body_text
- review_args = parse_commands(
- email_body_text, ['reviewer'])
- if len(review_args) > 0:
- cmd, args = review_args[0]
- review_request = (
- CodeEmailCommands.parseReviewRequest(cmd, args))
- review_requests.append(review_request)
-
- bmp = source.addLandingTarget(submitter, target,
- needs_review=True,
- description=description,
- review_requests=review_requests)
- return bmp
-
- except BranchMergeProposalExists:
- body = get_error_message(
- 'branchmergeproposal-exists.txt',
- error_templates=error_templates,
- source_branch=source.bzr_identity,
- target_branch=target.bzr_identity)
- simple_sendmail('merge@xxxxxxxxxxxxxxxxxx',
- [message.get('from')],
- 'Error Creating Merge Proposal', body)
- transaction.abort()
- except IncomingEmailError, error:
- send_process_error_notification(
- str(submitter.preferredemail.email),
- 'Submit Request Failure',
- error.message, email_body_text, error.failing_command)
- transaction.abort()
=== removed file 'lib/lp/code/mail/errortemplates/branch-creation-exception.txt'
--- lib/lp/code/mail/errortemplates/branch-creation-exception.txt 2011-08-11 22:18:34 +0000
+++ lib/lp/code/mail/errortemplates/branch-creation-exception.txt 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
-Launchpad cannot create the branch requested by your merge directive:
-%(reason)s
=== removed file 'lib/lp/code/mail/errortemplates/missingmergedirective.txt'
--- lib/lp/code/mail/errortemplates/missingmergedirective.txt 2011-08-11 22:18:34 +0000
+++ lib/lp/code/mail/errortemplates/missingmergedirective.txt 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
-Your email did not contain a merge directive. Please resend your email with
-the merge directive attached.
=== modified file 'lib/lp/code/mail/tests/test_codehandler.py'
--- lib/lp/code/mail/tests/test_codehandler.py 2012-01-20 15:42:44 +0000
+++ lib/lp/code/mail/tests/test_codehandler.py 2012-04-27 19:10:25 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Testing the CodeHandler."""
@@ -8,64 +8,38 @@
from difflib import unified_diff
from textwrap import dedent
-from bzrlib.branch import Branch
-from bzrlib.urlutils import join as urljoin
-from bzrlib.workingtree import WorkingTree
from storm.store import Store
import transaction
-from zope.component import getUtility
-from zope.interface import (
- directlyProvidedBy,
- directlyProvides,
- )
from zope.security.management import setSecurityPolicy
-from zope.security.proxy import removeSecurityProxy
from lp.code.enums import (
BranchMergeProposalStatus,
BranchSubscriptionNotificationLevel,
- BranchType,
- BranchVisibilityRule,
CodeReviewNotificationLevel,
CodeReviewVote,
)
-from lp.code.interfaces.branchlookup import IBranchLookup
from lp.code.mail.codehandler import (
AddReviewerEmailCommand,
CodeEmailCommands,
CodeHandler,
CodeReviewEmailCommandExecutionContext,
InvalidBranchMergeProposalAddress,
- MissingMergeDirective,
- NonLaunchpadTarget,
UpdateStatusEmailCommand,
VoteEmailCommand,
)
from lp.code.model.branchmergeproposaljob import (
BranchMergeProposalJob,
BranchMergeProposalJobType,
- CreateMergeProposalJob,
- MergeProposalNeedsReviewEmailJob,
)
from lp.code.model.diff import PreviewDiff
from lp.code.tests.helpers import make_merge_proposal_without_reviewers
-from lp.codehosting.vfs import get_lp_server
-from lp.registry.interfaces.person import IPersonSet
from lp.services.config import config
-from lp.services.job.runner import JobRunner
from lp.services.mail.handlers import mail_handlers
from lp.services.mail.interfaces import (
EmailProcessingError,
- IWeaklyAuthenticatedPrincipal,
)
from lp.services.messages.model.message import MessageSet
-from lp.services.osutils import override_environ
from lp.services.webapp.authorization import LaunchpadSecurityPolicy
-from lp.services.webapp.interaction import (
- get_current_principal,
- setupInteraction,
- )
-from lp.services.webapp.interfaces import IPlacelessAuthUtility
from lp.testing import (
login,
login_person,
@@ -249,7 +223,7 @@
review needs-fixing
- --
+ --\x20
For more information about using Launchpad by e-mail, see
https://help.launchpad.net/EmailInterface
or send an email to help@xxxxxxxxxxxxx"""),
@@ -394,286 +368,18 @@
self.assertRaises(InvalidBranchMergeProposalAddress,
self.code_handler.getBranchMergeProposal, 'mp+abc@')
- def test_acquireBranchesForProposal(self):
- """Ensure CodeHandler._acquireBranchesForProposal works."""
- target_branch = self.factory.makeAnyBranch()
- source_branch = self.factory.makeAnyBranch()
- md = self.factory.makeMergeDirective(source_branch, target_branch)
- submitter = self.factory.makePerson()
- switch_dbuser(config.processmail.dbuser)
- mp_source, mp_target = self.code_handler._acquireBranchesForProposal(
- md, submitter)
- self.assertEqual(mp_source, source_branch)
- self.assertEqual(mp_target, target_branch)
- transaction.commit()
-
- def test_acquireBranchesForProposalRemoteTarget(self):
- """CodeHandler._acquireBranchesForProposal fails on remote targets."""
- source_branch = self.factory.makeAnyBranch()
- md = self.factory.makeMergeDirective(
- source_branch, target_branch_url='http://example.com')
- submitter = self.factory.makePerson()
- switch_dbuser(config.create_merge_proposals.dbuser)
- self.assertRaises(
- NonLaunchpadTarget, self.code_handler._acquireBranchesForProposal,
- md, submitter)
- transaction.commit()
-
- def test_acquireBranchesForProposalRemoteSource(self):
- """CodeHandler._acquireBranchesForProposal allows remote sources.
-
- If there's no existing remote branch, it creates one, using
- the suffix of the url as a branch name seed.
- """
- target_branch = self.factory.makeProductBranch()
- source_branch_url = 'http://example.com/suffix'
- md = self.factory.makeMergeDirective(
- source_branch_url=source_branch_url, target_branch=target_branch)
- branches = getUtility(IBranchLookup)
- self.assertIs(None, branches.getByUrl(source_branch_url))
- submitter = self.factory.makePerson()
- switch_dbuser(config.create_merge_proposals.dbuser)
- mp_source, mp_target = self.code_handler._acquireBranchesForProposal(
- md, submitter)
- self.assertEqual(mp_target, target_branch)
- self.assertIsNot(None, mp_source)
- self.assertEqual(mp_source, branches.getByUrl(source_branch_url))
- self.assertEqual(BranchType.REMOTE, mp_source.branch_type)
- self.assertEqual(mp_target.product, mp_source.product)
- self.assertEqual('suffix', mp_source.name)
- transaction.commit()
-
- def test_acquireBranchesForProposalRemoteSourceDupeName(self):
- """CodeHandler._acquireBranchesForProposal creates names safely.
-
- When creating a new branch, it uses the suffix of the url as a branch
- name seed. If there is already a branch with that name, it appends
- a numeric suffix.
- """
- target_branch = self.factory.makeProductBranch()
- source_branch_url = 'http://example.com/suffix'
- md = self.factory.makeMergeDirective(
- source_branch_url=source_branch_url, target_branch=target_branch)
- submitter = self.factory.makePerson()
- self.factory.makeProductBranch(
- product=target_branch.product, name='suffix', owner=submitter)
- switch_dbuser(config.create_merge_proposals.dbuser)
- mp_source, mp_target = self.code_handler._acquireBranchesForProposal(
- md, submitter)
- self.assertEqual('suffix-1', mp_source.name)
- transaction.commit()
-
- def test_findMergeDirectiveAndComment(self):
- """findMergeDirectiveAndComment works."""
- md = self.factory.makeMergeDirective()
- message = self.factory.makeSignedMessage(
- body='Hi!\n', attachment_contents=''.join(md.to_lines()),
- force_transfer_encoding=True)
- code_handler = CodeHandler()
- switch_dbuser(config.processmail.dbuser)
- comment, md2 = code_handler.findMergeDirectiveAndComment(message)
- self.assertEqual('Hi!\n', comment)
- self.assertEqual(md.revision_id, md2.revision_id)
- self.assertEqual(md.target_branch, md2.target_branch)
- transaction.commit()
-
- def test_findMergeDirectiveAndCommentEmptyBody(self):
- """findMergeDirectiveAndComment handles empty message bodies.
-
- Empty message bodies are returned verbatim.
- """
- md = self.factory.makeMergeDirective()
- message = self.factory.makeSignedMessage(
- body='', attachment_contents=''.join(md.to_lines()))
- switch_dbuser(config.processmail.dbuser)
- code_handler = CodeHandler()
- comment, md2 = code_handler.findMergeDirectiveAndComment(message)
- self.assertEqual('', comment)
- transaction.commit()
-
- def test_findMergeDirectiveAndComment_no_content_type(self):
- """Parts with no content-type are treated as text/plain."""
- md = self.factory.makeMergeDirective()
- message = self.factory.makeSignedMessage(
- body='', attachment_contents=''.join(md.to_lines()))
- body = message.get_payload()[0]
- del body['Content-type']
- body.set_payload('body')
- switch_dbuser(config.processmail.dbuser)
- code_handler = CodeHandler()
- comment, md2 = code_handler.findMergeDirectiveAndComment(message)
- self.assertEqual('body', comment)
-
- def test_findMergeDirectiveAndComment_case_insensitive(self):
- """findMergeDirectiveAndComment uses case-insensitive content-type."""
- md = self.factory.makeMergeDirective()
- message = self.factory.makeSignedMessage(
- body='', attachment_contents=''.join(md.to_lines()))
- body = message.get_payload()[0]
- # Unlike dicts, messages append when you assign to a key. So
- # we must delete the first Content-type before adding another.
- del body['Content-type']
- body['Content-type'] = 'Text/Plain'
- body.set_payload('body')
- switch_dbuser(config.processmail.dbuser)
- code_handler = CodeHandler()
- comment, md2 = code_handler.findMergeDirectiveAndComment(message)
- self.assertEqual('body', comment)
-
- def test_findMergeDirectiveAndCommentUnicodeBody(self):
- """findMergeDirectiveAndComment returns unicode comments."""
- md = self.factory.makeMergeDirective()
- message = self.factory.makeSignedMessage(
- body=u'\u1234', attachment_contents=''.join(md.to_lines()))
- switch_dbuser(config.processmail.dbuser)
- code_handler = CodeHandler()
- comment, md2 = code_handler.findMergeDirectiveAndComment(message)
- self.assertEqual(u'\u1234', comment)
- transaction.commit()
-
- def test_findMergeDirectiveAndCommentNoMergeDirective(self):
- """findMergeDirectiveAndComment handles missing merge directives.
-
- MissingMergeDirective is raised when no merge directive is present.
- """
- message = self.factory.makeSignedMessage(body='Hi!\n')
- switch_dbuser(config.processmail.dbuser)
- code_handler = CodeHandler()
- self.assertRaises(MissingMergeDirective,
- code_handler.findMergeDirectiveAndComment, message)
- transaction.commit()
-
- def test_processMergeProposal(self):
- """processMergeProposal creates a merge proposal and comment."""
- message, file_alias, source, target = (
- self.factory.makeMergeDirectiveEmail())
- # Add some revisions so the proposal is ready.
- self.factory.makeRevisionsForBranch(source, count=1)
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- pop_notifications()
- bmp = code_handler.processMergeProposal(message)
- self.assertEqual(source, bmp.source_branch)
- self.assertEqual(target, bmp.target_branch)
- self.assertEqual('Hi!', bmp.description)
- # No emails are sent.
- messages = pop_notifications()
- self.assertEqual(0, len(messages))
- # Only a job created.
- runner = JobRunner.fromReady(MergeProposalNeedsReviewEmailJob)
- self.assertEqual(1, len(list(runner.jobs)))
- transaction.commit()
-
- def test_processMergeProposalEmptyMessage(self):
- """processMergeProposal handles empty message bodies.
-
- Messages with empty bodies produce merge proposals only, not
- comments.
- """
- message, file_alias, source_branch, target_branch = (
- self.factory.makeMergeDirectiveEmail(body=' '))
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- bmp = code_handler.processMergeProposal(message)
- self.assertEqual(source_branch, bmp.source_branch)
- self.assertEqual(target_branch, bmp.target_branch)
- self.assertIs(None, bmp.description)
- self.assertEqual(0, bmp.all_comments.count())
- transaction.commit()
-
- def test_processMergeDirectiveEmailNeedsGPG(self):
- """process creates a merge proposal from a merge directive email."""
- message, file_alias, source, target = (
- self.factory.makeMergeDirectiveEmail())
- # Ensure the message is stored in the librarian.
- # mail.incoming.handleMail also explicitly does this.
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- # In order to fake a non-gpg signed email, we say that the current
- # principal direcly provides IWeaklyAuthenticatePrincipal, which is
- # what the surrounding code does.
- cur_principal = get_current_principal()
- directlyProvides(
- cur_principal, directlyProvidedBy(cur_principal),
- IWeaklyAuthenticatedPrincipal)
- code_handler.process(message, 'merge@xxxxxxxxxxxxxxxxxx', file_alias)
-
- notification = pop_notifications()[0]
- self.assertEqual('Submit Request Failure', notification['subject'])
- # The returned message is a multipart message, the first part is
- # the message, and the second is the original message.
- message, original = notification.get_payload()
- self.assertEqual(dedent("""\
- An error occurred while processing a mail you sent to Launchpad's email
- interface.
-
-
- Error message:
-
- All emails to merge@xxxxxxxxxxxxxxxxxx must be signed with your OpenPGP
- key.
-
-
- --
- For more information about using Launchpad by e-mail, see
- https://help.launchpad.net/EmailInterface
- or send an email to help@xxxxxxxxxxxxx"""),
- message.get_payload(decode=True))
-
def test_processWithMergeDirectiveEmail(self):
- """process creates a merge proposal from a merge directive email."""
- message, file_alias, source, target = (
- self.factory.makeMergeDirectiveEmail())
- # Ensure the message is stored in the librarian.
- # mail.incoming.handleMail also explicitly does this.
- switch_dbuser(config.processmail.dbuser)
- code_handler = CodeHandler()
- self.assertEqual(0, source.landing_targets.count())
- code_handler.process(message, 'merge@xxxxxxxxxxxxxxxxxx', file_alias)
- switch_dbuser(config.create_merge_proposals.dbuser)
- JobRunner.fromReady(CreateMergeProposalJob).runAll()
- self.assertEqual(target, source.landing_targets[0].target_branch)
- # Ensure the DB operations violate no constraints.
- Store.of(source).flush()
-
- def test_processWithUnicodeMergeDirectiveEmail(self):
- """process creates a comment from a unicode message body."""
- message, file_alias, source, target = (
- self.factory.makeMergeDirectiveEmail(body=u'\u1234'))
- # Ensure the message is stored in the librarian.
- # mail.incoming.handleMail also explicitly does this.
- switch_dbuser(config.processmail.dbuser)
- code_handler = CodeHandler()
- self.assertEqual(0, source.landing_targets.count())
- code_handler.process(message, 'merge@xxxxxxxxxxxxxxxxxx', file_alias)
- switch_dbuser(config.create_merge_proposals.dbuser)
- JobRunner.fromReady(CreateMergeProposalJob).runAll()
- proposal = source.landing_targets[0]
- self.assertEqual(u'\u1234', proposal.description)
- # Ensure the DB operations violate no constraints.
- Store.of(proposal).flush()
-
- def test_processMergeProposalReviewerRequested(self):
- # The commands in the merge proposal are parsed.
- eric = self.factory.makePerson(name="eric")
- message, file_alias, source_branch, target_branch = (
- self.factory.makeMergeDirectiveEmail(body=dedent("""\
- This is the comment.
-
- reviewer eric
- """)))
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- pop_notifications()
- bmp = code_handler.processMergeProposal(message)
- pending_reviews = list(bmp.votes)
- self.assertEqual(1, len(pending_reviews))
- self.assertEqual(eric, pending_reviews[0].reviewer)
- # No emails are sent.
- messages = pop_notifications()
- self.assertEqual(0, len(messages))
- # Ensure the DB operations violate no constraints.
- Store.of(bmp).flush()
+ """process errors if merge@ address used."""
+ message = self.factory.makeSignedMessage()
+ file_alias = self.factory.makeLibraryFileAlias(
+ content=message.as_string())
+ # mail.incoming.handleMail also explicitly does this.
+ switch_dbuser(config.processmail.dbuser)
+ code_handler = CodeHandler()
+ code_handler.process(message, 'merge@xxxxxxxxxxxxxxxxxx', file_alias)
+ notification = pop_notifications()[0]
+ self.assertEqual(
+ 'Merge directive not supported.', notification['Subject'])
def test_reviewer_with_diff(self):
"""Requesting a review with a diff works."""
@@ -695,161 +401,6 @@
[vote] = bmp.votes
self.assertEqual(eric, vote.reviewer)
- def test_processMergeProposalDefaultReviewer(self):
- # If no reviewer was requested in the comment body, then the default
- # reviewer of the target branch is used.
- message, file_alias, source_branch, target_branch = (
- self.factory.makeMergeDirectiveEmail(body=dedent("""\
- This is the comment.
- """)))
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- pop_notifications()
- bmp = code_handler.processMergeProposal(message)
- # If no reviewer is specified, then the default reviewer of the target
- # branch is requested to review.
- pending_reviews = list(bmp.votes)
- self.assertEqual(1, len(pending_reviews))
- self.assertEqual(
- target_branch.code_reviewer,
- pending_reviews[0].reviewer)
- # No emails are sent.
- messages = pop_notifications()
- self.assertEqual(0, len(messages))
- # Ensure the DB operations violate no constraints.
- Store.of(target_branch).flush()
-
- def test_processMergeProposalExists(self):
- """processMergeProposal raises BranchMergeProposalExists
-
- If there is already a merge proposal with the same target and source
- branches of the merge directive, an email is sent to the user.
- """
- message, file_alias, source, target = (
- self.factory.makeMergeDirectiveEmail())
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- code_handler.processMergeProposal(message)
- pop_notifications()
- transaction.commit()
- code_handler.processMergeProposal(message)
- [notification] = pop_notifications()
- self.assertEqual(
- notification['Subject'], 'Error Creating Merge Proposal')
- self.assertEqual(
- notification.get_payload(decode=True),
- 'The branch %s is already proposed for merging into %s.\n\n'
- % (source.bzr_identity, target.bzr_identity))
- self.assertEqual(notification['to'], message['from'])
-
- def test_processMissingMergeDirective(self):
- """process sends an email if the original email lacks an attachment.
- """
- message = self.factory.makeSignedMessage(body='A body',
- subject='A subject', attachment_contents='')
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- code_handler.processMergeProposal(message)
- transaction.commit()
- [notification] = pop_notifications()
-
- self.assertEqual(
- notification['Subject'], 'Error Creating Merge Proposal')
- self.assertEqual(
- notification.get_payload(),
- 'Your email did not contain a merge directive. Please resend '
- 'your email with\nthe merge directive attached.\n')
- self.assertEqual(notification['to'],
- message['from'])
-
- def makeTargetBranch(self):
- """Helper for getNewBranchInfo tests."""
- owner = self.factory.makePerson(name='target-owner')
- product = self.factory.makeProduct(name='target-product')
- return self.factory.makeProductBranch(product=product, owner=owner)
-
- def test_getNewBranchInfoNoURL(self):
- """If no URL, target namespace is used, with 'merge' basename."""
- submitter = self.factory.makePerson(name='merge-submitter')
- target = self.makeTargetBranch()
- code_handler = CodeHandler()
- namespace, base = code_handler._getNewBranchInfo(
- None, target, submitter)
- self.assertEqual('~merge-submitter/target-product', namespace.name)
- self.assertEqual('merge', base)
-
- def test_getNewBranchInfoRemoteURL(self):
- """If a URL is provided, its base is used, with target namespace."""
- submitter = self.factory.makePerson(name='merge-submitter')
- target = self.makeTargetBranch()
- code_handler = CodeHandler()
- namespace, base = code_handler._getNewBranchInfo(
- 'http://foo/bar', target, submitter)
- self.assertEqual('~merge-submitter/target-product', namespace.name)
- self.assertEqual('bar', base)
-
- def test_getNewBranchInfoRemoteURLTrailingSlash(self):
- """Trailing slashes are ignored when determining base."""
- submitter = self.factory.makePerson(name='merge-submitter')
- target = self.makeTargetBranch()
- code_handler = CodeHandler()
- namespace, base = code_handler._getNewBranchInfo(
- 'http://foo/bar/', target, submitter)
- self.assertEqual('~merge-submitter/target-product', namespace.name)
- self.assertEqual('bar', base)
-
- def test_getNewBranchInfoLPURL(self):
- """If an LP URL is provided, we attempt to reproduce it exactly."""
- submitter = self.factory.makePerson(name='merge-submitter')
- target = self.makeTargetBranch()
- self.factory.makeProduct('uproduct')
- self.factory.makePerson(name='uuser')
- code_handler = CodeHandler()
- namespace, base = code_handler._getNewBranchInfo(
- config.codehosting.supermirror_root + '~uuser/uproduct/bar',
- target, submitter)
- self.assertEqual('~uuser/uproduct', namespace.name)
- self.assertEqual('bar', base)
-
- def test_getNewBranchInfoLPURLTrailingSlash(self):
- """Trailing slashes are permitted in LP URLs."""
- submitter = self.factory.makePerson(name='merge-submitter')
- target = self.makeTargetBranch()
- self.factory.makeProduct('uproduct')
- self.factory.makePerson(name='uuser')
- code_handler = CodeHandler()
- namespace, base = code_handler._getNewBranchInfo(
- config.codehosting.supermirror_root + '~uuser/uproduct/bar/',
- target, submitter)
- self.assertEqual('~uuser/uproduct', namespace.name)
- self.assertEqual('bar', base)
-
- def test_processNonLaunchpadTarget(self):
- """When target branch is unknown to Launchpad, the user is notified.
- """
- directive = self.factory.makeMergeDirective(
- target_branch_url='http://www.example.com')
- message = self.factory.makeSignedMessage(body='body',
- subject='This is gonna fail', attachment_contents=''.join(
- directive.to_lines()))
-
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- code_handler.processMergeProposal(message)
- transaction.commit()
- [notification] = pop_notifications()
-
- self.assertEqual(
- notification['Subject'], 'Error Creating Merge Proposal')
- self.assertEqual(
- notification.get_payload(decode=True),
- 'The target branch at %s is not known to Launchpad. It\'s\n'
- 'possible that your submit branch is not set correctly, or that '
- 'your submit\nbranch has not yet been pushed to Launchpad.\n\n'
- % ('http://www.example.com'))
- self.assertEqual(notification['to'],
- message['from'])
-
def test_processMissingSubject(self):
"""If the subject is missing, the user is warned by email."""
mail = self.factory.makeSignedMessage(
@@ -874,267 +425,6 @@
self.assertEqual(0, bmp.all_comments.count())
-class TestCodeHandlerProcessMergeDirective(TestCaseWithFactory):
- """Test the merge directive processing parts of the code email hander."""
-
- layer = ZopelessAppServerLayer
-
- def setUp(self):
- TestCaseWithFactory.setUp(self, user='test@xxxxxxxxxxxxx')
- self.code_handler = CodeHandler()
- self._old_policy = setSecurityPolicy(LaunchpadSecurityPolicy)
-
- def tearDown(self):
- setSecurityPolicy(self._old_policy)
- TestCaseWithFactory.tearDown(self)
-
- def _createTargetSourceAndBundle(self, format=None):
- """Create a merge directive with a bundle and associated branches.
-
- The target branch is created in the specified format, or the default
- format if the format is None.
-
- :return: A tuple containing the db_branch relating to the target
- branch, a bzr_branch of the source branch, and the merge directive
- containing the revisions in the source branch that aren't in the
- target branch.
- """
- db_target_branch, target_tree = self.create_branch_and_tree(
- tree_location='.', format=format)
- target_tree.branch.set_public_branch(db_target_branch.bzr_identity)
- # XXX: AaronBentley 2010-08-06 bug=614404: a bzr username is
- # required to generate the revision-id.
- with override_environ(BZR_EMAIL='me@xxxxxxxxxxx'):
- target_tree.commit('rev1')
- # Make sure that the created branch has been mirrored.
- removeSecurityProxy(db_target_branch).branchChanged(
- '', 'rev1', None, None, None)
- sprout_bzrdir = target_tree.bzrdir.sprout('source')
- source_tree = sprout_bzrdir.open_workingtree()
- source_tree.commit('rev2')
- message = self.factory.makeBundleMergeDirectiveEmail(
- source_tree.branch, db_target_branch)
- return db_target_branch, source_tree.branch, message
-
- def _openBazaarBranchAsClient(self, db_branch):
- """Open the Bazaar branch relating to db_branch as if a client was.
-
- The client has write access to the branch.
- """
- lp_server = get_lp_server(db_branch.owner.id)
- lp_server.start_server()
- self.addCleanup(lp_server.stop_server)
- branch_url = urljoin(lp_server.get_url(), db_branch.unique_name)
- return Branch.open(branch_url)
-
- def _processMergeDirective(self, message):
- """Process the merge directive email."""
- switch_dbuser(config.create_merge_proposals.dbuser)
- code_handler = CodeHandler()
- # Do the authentication dance as we do in the processing script.
- authutil = getUtility(IPlacelessAuthUtility)
- email_addr = message['from']
- principal = authutil.getPrincipalByLogin(email_addr)
- if principal is None:
- raise AssertionError('No principal found for %s' % email_addr)
- setupInteraction(principal, email_addr)
- return code_handler.processMergeProposal(message)
-
- def test_nonstackable_target(self):
- # If the target branch is in a non-stackable format, then the source
- # branch that is created is an empty hosted branch. The new branch
- # will not have a mirror requested as there are no revisions, and
- # there is no branch created in the hosted area.
-
- # XXX Tim Penhey 2010-07-27 bug 610292
- # We should fail here and return an oops email to the user.
- self.useBzrBranches()
- branch, source, message = self._createTargetSourceAndBundle(
- format="pack-0.92")
- bmp = self._processMergeDirective(message)
- self.assertEqual(BranchType.HOSTED, bmp.source_branch.branch_type)
- self.assertIs(None, bmp.source_branch.next_mirror_time)
-
- def test_stackable_unmirrored_target(self):
- # If the target branch is in a stackable format but has not been
- # mirrored, the source branch that is created is an empty hosted
- # branch. The new branch will not have a mirror requested as there
- # are no revisions, and there is no branch created in the hosted area.
- self.useBzrBranches()
- branch, source, message = self._createTargetSourceAndBundle(
- format="1.9")
- # Mark the target branch as "unmirrored", at least as far as the db is
- # concerned.
- branch.last_mirrored = None
- branch.last_mirrored_id = None
- bmp = self._processMergeDirective(message)
- self.assertEqual(BranchType.REMOTE, bmp.source_branch.branch_type)
-
- def test_stackable_target(self):
- # If the target branch is in a stackable format, then the source
- # branch that is created is a hosted branch stacked on the target
- # branch. The source branch will have the revisions from the bundle,
- # and a mirror will have been triggered.
- self.useBzrBranches()
- branch, source, message = self._createTargetSourceAndBundle(
- format="1.9")
- bmp = self._processMergeDirective(message)
- source_bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)
- self.assertEqual(BranchType.HOSTED, bmp.source_branch.branch_type)
- self.assertTrue(bmp.source_branch.pending_writes)
- self.assertEqual(
- source.last_revision(), source_bzr_branch.last_revision())
-
- def test_branch_stacked(self):
- # When a branch is created for a merge directive, it is created
- # stacked on the target branch.
- self.useBzrBranches()
- branch, source, message = self._createTargetSourceAndBundle(
- format="1.9")
- bmp = self._processMergeDirective(message)
- # The source branch is stacked on the target.
- source_bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)
- self.assertEqual(
- '/' + bmp.target_branch.unique_name,
- source_bzr_branch.get_stacked_on_url())
- # Make sure that the source branch doesn't have all the revisions.
- source_branch_revisions = (
- source_bzr_branch.bzrdir.open_repository().all_revision_ids())
- # The only revision is the tip revision, as the other revisions are
- # from the target branch.
- tip_revision = source_bzr_branch.last_revision()
- self.assertEqual([tip_revision], source_branch_revisions)
-
- def test_source_not_newer(self):
- # The source branch is created correctly when the source is not newer
- # than the target, instead of raising DivergedBranches.
- self.useBzrBranches()
- branch, source, message = self._createTargetSourceAndBundle(
- format="1.9")
- target_tree = WorkingTree.open('.')
- # XXX: AaronBentley 2010-08-06 bug=614404: a bzr username is
- # required to generate the revision-id.
- with override_environ(BZR_EMAIL='me@xxxxxxxxxxx'):
- target_tree.commit('rev2b')
- bmp = self._processMergeDirective(message)
- lp_branch = self._openBazaarBranchAsClient(bmp.source_branch)
- self.assertEqual(source.last_revision(), lp_branch.last_revision())
-
- def _createPreexistingSourceAndMessage(self, target_format,
- source_format, set_stacked=False):
- """Create the source and target branches and the merge directive."""
- db_target_branch, target_tree = self.create_branch_and_tree(
- 'target', format=target_format)
- target_tree.branch.set_public_branch(db_target_branch.bzr_identity)
- # XXX: AaronBentley 2010-08-06 bug=614404: a bzr username is
- # required to generate the revision-id.
- with override_environ(BZR_EMAIL='me@xxxxxxxxxxx'):
- revid = target_tree.commit('rev1')
- removeSecurityProxy(db_target_branch).branchChanged(
- '', revid, None, None, None)
-
- db_source_branch, source_tree = self.create_branch_and_tree(
- 'lpsource', db_target_branch.product, format=source_format)
- # The branch is not scheduled to be mirrorred.
- self.assertIs(db_source_branch.next_mirror_time, None)
- source_tree.pull(target_tree.branch)
- source_tree.commit('rev2', rev_id='rev2')
- # bundle_tree is effectively behaving like a local copy of
- # db_source_branch, and is used to create the merge directive.
- sprout_bzrdir = source_tree.bzrdir.sprout('source')
- bundle_tree = sprout_bzrdir.open_workingtree()
- bundle_tree.commit('rev3', rev_id='rev3')
- bundle_tree.branch.set_public_branch(db_source_branch.bzr_identity)
- message = self.factory.makeBundleMergeDirectiveEmail(
- bundle_tree.branch, db_target_branch,
- sender=db_source_branch.owner)
- # Tell the source branch that it is stacked on the target.
- if set_stacked:
- stacked_url = '/' + db_target_branch.unique_name
- branch = self._openBazaarBranchAsClient(db_source_branch)
- branch.set_stacked_on_url(stacked_url)
- return db_source_branch, message
-
- def test_existing_stacked_branch(self):
- # A bundle can update an existing branch if they are both stackable,
- # and the source branch is stacked.
- self.useBzrBranches()
- lp_source, message = self._createPreexistingSourceAndMessage(
- target_format="1.9", source_format="1.9", set_stacked=True)
- bmp = self._processMergeDirective(message)
- # The branch merge proposal should use the existing db branch.
- self.assertEqual(lp_source, bmp.source_branch)
- bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)
- # The branch has been updated.
- self.assertEqual('rev3', bzr_branch.last_revision())
-
- def test_existing_unstacked_branch(self):
- # Even if the source and target are stackable, if the source is not
- # stacked, we don't support stacking something that wasn't stacked
- # before (yet).
- self.useBzrBranches()
- lp_source, message = self._createPreexistingSourceAndMessage(
- target_format="1.9", source_format="1.9")
- bmp = self._processMergeDirective(message)
- # The branch merge proposal should use the existing db branch.
- self.assertEqual(lp_source, bmp.source_branch)
- bzr_branch = self._openBazaarBranchAsClient(bmp.source_branch)
- # The hosted copy of the branch has not been updated.
- self.assertEqual('rev2', bzr_branch.last_revision())
-
- def test_existing_branch_nonstackable_target(self):
- # If the target branch is not stackable, then we don't pull any
- # revisions.
- self.useBzrBranches()
- lp_source, message = self._createPreexistingSourceAndMessage(
- target_format="pack-0.92", source_format="1.9")
- bmp = self._processMergeDirective(message)
- # The branch merge proposal should use the existing db branch.
- self.assertEqual(lp_source, bmp.source_branch)
- # Now the branch is not scheduled to be mirrorred.
- self.assertIs(None, lp_source.next_mirror_time)
- hosted = self._openBazaarBranchAsClient(bmp.source_branch)
- # The hosted copy has not been updated.
- self.assertEqual('rev2', hosted.last_revision())
-
- def test_existing_branch_nonstackable_source(self):
- # If the source branch is not stackable, then we don't pull any
- # revisions.
- self.useBzrBranches()
- lp_source, message = self._createPreexistingSourceAndMessage(
- target_format="1.9", source_format="pack-0.92")
- bmp = self._processMergeDirective(message)
- # The branch merge proposal should use the existing db branch.
- self.assertEqual(lp_source, bmp.source_branch)
- # Now the branch is not scheduled to be mirrorred.
- self.assertIs(None, lp_source.next_mirror_time)
- hosted = self._openBazaarBranchAsClient(bmp.source_branch)
- # The hosted copy has not been updated.
- self.assertEqual('rev2', hosted.last_revision())
-
- def test_forbidden_target(self):
- """Specifying a branch in a forbidden target generates email."""
- self.useBzrBranches()
- branch, source, message = self._createTargetSourceAndBundle(
- format="pack-0.92")
- branch.product.setBranchVisibilityTeamPolicy(
- None, BranchVisibilityRule.FORBIDDEN)
- result = self._processMergeDirective(message)
- self.assertIs(None, result)
- notifications = pop_notifications()
- self.assertEqual(1, len(notifications))
- self.assertEqual(
- 'Error Creating Merge Proposal', notifications[0]['subject'])
- body = notifications[0].get_payload(decode=True)
- sender = getUtility(IPersonSet).getByEmail(message['from'])
- expected = (
- 'Launchpad cannot create the branch requested by'
- ' your merge directive:\n'
- 'You cannot create branches in "~%s/%s"\n' %
- (sender.name, branch.product.name))
- self.assertEqual(expected, body)
-
-
class TestVoteEmailCommand(TestCase):
"""Test the vote and tag processing of the VoteEmailCommand."""
=== modified file 'lib/lp/code/model/branchmergeproposaljob.py'
--- lib/lp/code/model/branchmergeproposaljob.py 2012-04-13 19:20:03 +0000
+++ lib/lp/code/model/branchmergeproposaljob.py 2012-04-27 19:10:25 +0000
@@ -17,7 +17,6 @@
'BranchMergeProposalJobSource',
'BranchMergeProposalJobType',
'CodeReviewCommentEmailJob',
- 'CreateMergeProposalJob',
'GenerateIncrementalDiffJob',
'MergeProposalNeedsReviewEmailJob',
'MergeProposalUpdatedEmailJob',
@@ -30,7 +29,6 @@
datetime,
timedelta,
)
-from email.utils import parseaddr
from lazr.delegates import delegates
from lazr.enum import (
@@ -69,8 +67,6 @@
IBranchMergeProposalJobSource,
ICodeReviewCommentEmailJob,
ICodeReviewCommentEmailJobSource,
- ICreateMergeProposalJob,
- ICreateMergeProposalJobSource,
IGenerateIncrementalDiffJob,
IGenerateIncrementalDiffJobSource,
IMergeProposalNeedsReviewEmailJob,
@@ -91,7 +87,6 @@
from lp.codehosting.bzrutils import server
from lp.codehosting.vfs import (
get_ro_server,
- get_rw_server,
)
from lp.registry.interfaces.person import IPersonSet
from lp.services.config import config
@@ -107,16 +102,9 @@
BaseRunnableJobSource,
)
from lp.services.mail.sendmail import format_address_for_person
-from lp.services.messages.interfaces.message import IMessageJob
-from lp.services.messages.model.message import (
- MessageJob,
- MessageJobAction,
- )
from lp.services.webapp import errorlog
-from lp.services.webapp.interaction import setupInteraction
from lp.services.webapp.interfaces import (
DEFAULT_FLAVOR,
- IPlacelessAuthUtility,
IStoreSelector,
MAIN_STORE,
MASTER_FLAVOR,
@@ -403,83 +391,6 @@
return format_address_for_person(registrant)
-class CreateMergeProposalJob(BaseRunnableJob):
- """See `ICreateMergeProposalJob` and `ICreateMergeProposalJobSource`."""
-
- classProvides(ICreateMergeProposalJobSource)
-
- delegates(IMessageJob)
-
- class_action = MessageJobAction.CREATE_MERGE_PROPOSAL
-
- implements(ICreateMergeProposalJob)
-
- def __init__(self, context):
- """Create an instance of CreateMergeProposalJob.
-
- :param context: a MessageJob.
- """
- self.context = context
-
- def __eq__(self, other):
- return (self.__class__ == other.__class__ and
- self.context == other.context)
-
- @classmethod
- def create(klass, message_bytes):
- """See `ICreateMergeProposalJobSource`."""
- context = MessageJob(
- message_bytes, MessageJobAction.CREATE_MERGE_PROPOSAL)
- return klass(context)
-
- @classmethod
- def iterReady(klass):
- """Iterate through all ready BranchMergeProposalJobs."""
- store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
- jobs = store.find(
- (MessageJob),
- And(MessageJob.action == klass.class_action,
- MessageJob.job == Job.id,
- Job.id.is_in(Job.ready_jobs)))
- return (klass(job) for job in jobs)
-
- def run(self):
- """See `ICreateMergeProposalJob`."""
- # Avoid circular import
- from lp.code.mail.codehandler import CodeHandler
- url = self.context.message_bytes.getURL()
- with errorlog.globalErrorUtility.oopsMessage('Mail url: %r' % url):
- message = self.getMessage()
- # Since the message was checked as signed before it was saved in
- # the Librarian, just create the principal from the sender and set
- # up the interaction.
- name, email_addr = parseaddr(message['From'])
- authutil = getUtility(IPlacelessAuthUtility)
- principal = authutil.getPrincipalByLogin(email_addr)
- if principal is None:
- raise AssertionError('No principal found for %s' % email_addr)
- setupInteraction(principal, email_addr)
-
- server = get_rw_server()
- server.start_server()
- try:
- return CodeHandler().processMergeProposal(message)
- finally:
- server.stop_server()
-
- def getOopsRecipients(self):
- message = self.getMessage()
- from_ = message['From']
- if from_ is None:
- return []
- return [from_]
-
- def getOperationDescription(self):
- message = self.getMessage()
- return ('creating a merge proposal from message with subject %s' %
- message['Subject'])
-
-
class CodeReviewCommentEmailJob(BranchMergeProposalJobDerived):
"""A job to send a code review comment.
=== modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py'
--- lib/lp/code/model/tests/test_branchmergeproposal.py 2012-02-27 00:49:48 +0000
+++ lib/lp/code/model/tests/test_branchmergeproposal.py 2012-04-27 19:10:25 +0000
@@ -47,8 +47,6 @@
BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,
IBranchMergeProposal,
IBranchMergeProposalGetter,
- ICreateMergeProposalJob,
- ICreateMergeProposalJobSource,
notify_modified,
)
from lp.code.model.branchmergeproposal import (
@@ -57,7 +55,6 @@
)
from lp.code.model.branchmergeproposaljob import (
BranchMergeProposalJob,
- CreateMergeProposalJob,
MergeProposalNeedsReviewEmailJob,
UpdatePreviewDiffJob,
)
@@ -68,7 +65,6 @@
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.product import IProductSet
from lp.services.database.constants import UTC_NOW
-from lp.services.messages.interfaces.message import IMessageJob
from lp.services.webapp import canonical_url
from lp.services.webapp.testing import verifyObject
from lp.testing import (
@@ -82,14 +78,11 @@
ws_object,
)
from lp.testing.factory import (
- GPGSigningContext,
LaunchpadObjectFactory,
)
-from lp.testing.gpgkeys import import_secret_test_key
from lp.testing.layers import (
DatabaseFunctionalLayer,
LaunchpadFunctionalLayer,
- LaunchpadZopelessLayer,
)
@@ -1749,66 +1742,6 @@
BranchMergeProposalStatus.REJECTED, first_mp.queue_status)
-class TestCreateMergeProposalJob(TestCaseWithFactory):
- """Tests for CreateMergeProposalJob."""
-
- layer = LaunchpadZopelessLayer
-
- def setUp(self):
- TestCaseWithFactory.setUp(self, user='test@xxxxxxxxxxxxx')
-
- def test_providesInterface(self):
- """The class and instances correctly implement their interfaces."""
- verifyObject(ICreateMergeProposalJobSource, CreateMergeProposalJob)
- file_alias = self.factory.makeMergeDirectiveEmail()[1]
- job = CreateMergeProposalJob.create(file_alias)
- job.context.sync()
- verifyObject(IMessageJob, job)
- verifyObject(ICreateMergeProposalJob, job)
-
- def test_run_creates_proposal(self):
- """CreateMergeProposalJob.run should create a merge proposal."""
- key = import_secret_test_key()
- signing_context = GPGSigningContext(key.fingerprint, password='test')
- message, file_alias, source, target = (
- self.factory.makeMergeDirectiveEmail(
- signing_context=signing_context))
- job = CreateMergeProposalJob.create(file_alias)
- transaction.commit()
- proposal = job.run()
- self.assertEqual(proposal.source_branch, source)
- self.assertEqual(proposal.target_branch, target)
-
- def test_getOopsMailController(self):
- """The sender is notified when creating a bmp from email fails."""
- key = import_secret_test_key()
- signing_context = GPGSigningContext(key.fingerprint, password='test')
- message, file_alias, source, target = (
- self.factory.makeMergeDirectiveEmail(
- signing_context=signing_context))
- job = CreateMergeProposalJob.create(file_alias)
- transaction.commit()
- ctrl = job.getOopsMailController('1234')
- self.assertEqual([message['From']], ctrl.to_addrs)
- desc = ('creating a merge proposal from message with subject %s' %
- message['Subject'])
- self.assertIn(desc, ctrl.body)
-
- def test_iterReady_includes_ready_jobs(self):
- """Ready jobs should be listed."""
- file_alias = self.factory.makeMergeDirectiveEmail()[1]
- job = CreateMergeProposalJob.create(file_alias)
- self.assertEqual([job], list(CreateMergeProposalJob.iterReady()))
-
- def test_iterReady_excludes_unready_jobs(self):
- """Unready jobs should not be listed."""
- file_alias = self.factory.makeMergeDirectiveEmail()[1]
- job = CreateMergeProposalJob.create(file_alias)
- job.job.start()
- job.job.complete()
- self.assertEqual([], list(CreateMergeProposalJob.iterReady()))
-
-
class TestUpdatePreviewDiff(TestCaseWithFactory):
"""Test the updateMergeDiff method of BranchMergeProposal."""
=== removed file 'lib/lp/code/scripts/tests/test_create_merge_proposals.py'
--- lib/lp/code/scripts/tests/test_create_merge_proposals.py 2012-03-28 11:36:07 +0000
+++ lib/lp/code/scripts/tests/test_create_merge_proposals.py 1970-01-01 00:00:00 +0000
@@ -1,107 +0,0 @@
-#! /usr/bin/python
-#
-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Test the create_merge_proposals script"""
-
-from cStringIO import StringIO
-
-import transaction
-from zope.component import getUtility
-
-from lp.code.model.branchmergeproposaljob import CreateMergeProposalJob
-from lp.services.librarian.interfaces import ILibraryFileAliasSet
-from lp.services.scripts.tests import run_script
-from lp.testing import TestCaseWithFactory
-from lp.testing.factory import GPGSigningContext
-from lp.testing.gpgkeys import import_secret_test_key
-from lp.testing.layers import ZopelessAppServerLayer
-
-
-class TestCreateMergeProposals(TestCaseWithFactory):
-
- layer = ZopelessAppServerLayer
-
- def test_create_merge_proposals(self):
- """Ensure create_merge_proposals runs and creates proposals."""
- key = import_secret_test_key()
- signing_context = GPGSigningContext(key.fingerprint, password='test')
- email, file_alias, source, target = (
- self.factory.makeMergeDirectiveEmail(
- signing_context=signing_context))
- job = CreateMergeProposalJob.create(file_alias)
- self.assertEqual(0, source.landing_targets.count())
- transaction.commit()
- retcode, stdout, stderr = run_script(
- 'cronscripts/create_merge_proposals.py', [])
- self.assertEqual(0, retcode)
- self.assertTextMatchesExpressionIgnoreWhitespace(
- "INFO Creating lockfile: "
- "/var/lock/launchpad-create_merge_proposals.lock\n"
- "INFO Running <.*CreateMergeProposalJob object at .*?> "
- "\(ID %s\) in status Waiting\n"
- "INFO Ran 1 CreateMergeProposalJobs.\n" % job.job.id, stderr)
- self.assertEqual('', stdout)
- self.assertEqual(1, source.landing_targets.count())
-
- def createJob(self, branch, tree):
- """Create merge directive job from this branch."""
- tree.branch.set_public_branch(branch.bzr_identity)
- tree.commit('rev1')
- source = tree.bzrdir.sprout('source').open_workingtree()
- source.commit('rev2')
- message = self.factory.makeBundleMergeDirectiveEmail(
- source.branch, branch)
- message_str = message.as_string()
- library_file_aliases = getUtility(ILibraryFileAliasSet)
- file_alias = library_file_aliases.create(
- '*', len(message_str), StringIO(message_str), '*')
- CreateMergeProposalJob.create(file_alias)
- return source
-
- def jobOutputCheck(self, branch, source):
- """Run the job and check the output."""
- transaction.commit()
- retcode, stdout, stderr = run_script(
- 'cronscripts/create_merge_proposals.py', [])
- self.assertEqual(0, retcode)
- self.assertEqual(
- "INFO Creating lockfile: "
- "/var/lock/launchpad-create_merge_proposals.lock\n"
- "INFO Ran 1 CreateMergeProposalJobs.\n", stderr)
- self.assertEqual('', stdout)
- bmp = branch.landing_candidates[0]
- local_source = bmp.source_branch.getBzrBranch()
- # The branch has the correct last revision.
- self.assertEqual(
- source.branch.last_revision(), local_source.last_revision())
-
- def disabled_test_merge_directive_with_bundle(self):
- """Merge directives with bundles generate branches."""
- # XXX TimPenhey 2009-04-01 bug 352800
- self.useBzrBranches()
- branch, tree = self.create_branch_and_tree()
- source = self.createJob(branch, tree)
- self.jobOutputCheck(branch, source)
-
- def disabled_test_merge_directive_with_project(self):
- """Bundles are handled when the target branch has a project."""
- # XXX TimPenhey 2009-04-01 bug 352800
- self.useBzrBranches()
- product = self.factory.makeProduct(project=self.factory.makeProject())
- branch, tree = self.create_branch_and_tree(product=product)
- source = self.createJob(branch, tree)
- self.jobOutputCheck(branch, source)
-
- def test_oops(self):
- """A bogus request should cause an oops, not an exception."""
- file_alias = self.factory.makeLibraryFileAlias(content='bogus')
- CreateMergeProposalJob.create(file_alias)
- transaction.commit()
- retcode, stdout, stderr = run_script(
- 'cronscripts/create_merge_proposals.py', [])
- self.assertIn('INFO Creating lockfile:', stderr)
- self.assertIn('INFO Job resulted in OOPS:', stderr)
- self.assertIn('INFO Ran 0 CreateMergeProposalJobs.\n', stderr)
- self.assertEqual('', stdout)
=== modified file 'lib/lp/services/config/schema-lazr.conf'
--- lib/lp/services/config/schema-lazr.conf 2012-04-18 16:23:23 +0000
+++ lib/lp/services/config/schema-lazr.conf 2012-04-27 19:10:25 +0000
@@ -481,18 +481,6 @@
# datatype: string; a url
licensing_policy_url: https://help.launchpad.net/Legal/ProjectLicensing
-
-[create_merge_proposals]
-# The database user which will be used by this process.
-# datatype: string
-dbuser: create-merge-proposals
-
-# See [error_reports].
-error_dir: none
-
-# See [error_reports].
-oops_prefix: none
-
[packaging_translations]
# The database user which will be used by this process.
# datatype: string
=== added file 'lib/lp/services/mail/errortemplates/mergedirectivenotsupported.txt'
--- lib/lp/services/mail/errortemplates/mergedirectivenotsupported.txt 1970-01-01 00:00:00 +0000
+++ lib/lp/services/mail/errortemplates/mergedirectivenotsupported.txt 2012-04-27 19:10:25 +0000
@@ -0,0 +1,3 @@
+Launchpad no longer accepts merge directives, because this functionality was not being used. We apologise for the inconvenience. See https://bugs.launchpad.net/launchpad/+bug/989831 for details.
+
+To create merge proposals from the commandline, consider using "bzr lp-propose-merge". This command is from the launchpad plugin which usually ships with Bazaar. Of course, you can also use the Launchpad web site to create merge proposals.
=== modified file 'lib/lp/services/messages/interfaces/message.py'
--- lib/lp/services/messages/interfaces/message.py 2012-01-06 19:48:06 +0000
+++ lib/lp/services/messages/interfaces/message.py 2012-04-27 19:10:25 +0000
@@ -11,7 +11,6 @@
'IIndexedMessage',
'IMessage',
'IMessageChunk',
- 'IMessageJob',
'IMessageSet',
'IUserToUserEmail',
'IndexedMessage',
@@ -47,7 +46,6 @@
from lp import _
from lp.app.errors import NotFoundError
-from lp.services.job.interfaces.job import IJob
from lp.services.librarian.interfaces import ILibraryFileAlias
@@ -287,22 +285,6 @@
"""
-class IMessageJob(Interface):
- """Interface for jobs triggered by messages."""
-
- job = Object(schema=IJob, required=True)
-
- message_bytes = Object(
- title=_('Full MIME content of Email.'), required=True,
- schema=ILibraryFileAlias)
-
- def getMessage():
- """Return an email.Message representing this job's message."""
-
- def destroySelf():
- """Remove this object (and its job) from the database."""
-
-
class UnknownSender(NotFoundError):
"""Raised if we cannot lookup an email message's sender in the database"""
=== modified file 'lib/lp/services/messages/model/message.py'
--- lib/lp/services/messages/model/message.py 2012-04-13 07:19:14 +0000
+++ lib/lp/services/messages/model/message.py 2012-04-27 19:10:25 +0000
@@ -8,8 +8,6 @@
'DirectEmailAuthorization',
'Message',
'MessageChunk',
- 'MessageJob',
- 'MessageJobAction',
'MessageSet',
'UserToUserEmail',
]
@@ -32,10 +30,6 @@
import os.path
from lazr.config import as_timedelta
-from lazr.enum import (
- DBEnumeratedType,
- DBItem,
- )
import pytz
from sqlobject import (
BoolCol,
@@ -67,17 +61,13 @@
from lp.services.config import config
from lp.services.database.constants import UTC_NOW
from lp.services.database.datetimecol import UtcDateTimeCol
-from lp.services.database.enumcol import EnumCol
from lp.services.database.sqlbase import SQLBase
from lp.services.encoding import guess as ensure_unicode
-from lp.services.job.model.job import Job
from lp.services.librarian.interfaces import ILibraryFileAliasSet
-from lp.services.mail.signedmessage import signed_message_from_string
from lp.services.messages.interfaces.message import (
IDirectEmailAuthorization,
IMessage,
IMessageChunk,
- IMessageJob,
IMessageSet,
InvalidEmailMessage,
IUserToUserEmail,
@@ -637,59 +627,6 @@
Store.of(sender).add(self)
-class MessageJobAction(DBEnumeratedType):
- """MessageJob action
-
- The action that a job should perform.
- """
-
- CREATE_MERGE_PROPOSAL = DBItem(1, """
- Create a merge proposal.
-
- Create a merge proposal from a message which must contain a merge
- directive.
- """)
-
-
-class MessageJob(Storm):
- """A job for processing messages."""
-
- implements(IMessageJob)
- # XXX: AaronBentley 2009-02-05 bug=325883: This table is poorly named.
- __storm_table__ = 'MergeDirectiveJob'
-
- id = Int(primary=True)
-
- jobID = Int('job', allow_none=False)
- job = Reference(jobID, Job.id)
-
- message_bytesID = Int('merge_directive', allow_none=False)
- message_bytes = Reference(message_bytesID, 'LibraryFileAlias.id')
-
- action = EnumCol(enum=MessageJobAction)
-
- def __init__(self, message_bytes, action):
- Storm.__init__(self)
- self.job = Job()
- self.message_bytes = message_bytes
- self.action = action
-
- def destroySelf(self):
- """See `IMessageJob`."""
- self.job.destroySelf()
- Store.of(self).remove(self)
-
- def sync(self):
- """Update the database with all changes for this object."""
- store = Store.of(self)
- store.flush()
- store.autoreload(self)
-
- def getMessage(self):
- """See `IMessageJob`."""
- return signed_message_from_string(self.message_bytes.read())
-
-
class DirectEmailAuthorization:
"""See `IDirectEmailAuthorization`."""
=== modified file 'lib/lp/services/messages/tests/test_message.py'
--- lib/lp/services/messages/tests/test_message.py 2012-01-01 02:58:52 +0000
+++ lib/lp/services/messages/tests/test_message.py 2012-04-27 19:10:25 +0000
@@ -3,7 +3,6 @@
__metaclass__ = type
-from cStringIO import StringIO
from email.header import Header
from email.message import Message
from email.mime.multipart import MIMEMultipart
@@ -13,24 +12,14 @@
make_msgid,
)
-from sqlobject import SQLObjectNotFound
import transaction
-from zope.component import getUtility
-from lp.services.job.model.job import Job
-from lp.services.librarian.interfaces import ILibraryFileAliasSet
-from lp.services.mail.sendmail import MailController
-from lp.services.messages.interfaces.message import IMessageJob
from lp.services.messages.model.message import (
- MessageJob,
- MessageJobAction,
MessageSet,
)
-from lp.services.webapp.testing import verifyObject
from lp.testing import (
login,
TestCase,
- TestCaseWithFactory,
)
from lp.testing.factory import LaunchpadObjectFactory
from lp.testing.layers import LaunchpadFunctionalLayer
@@ -200,41 +189,3 @@
'Treating unknown encoding "booga" as latin-1.'):
result = MessageSet.decode(self.high_characters, 'booga')
self.assertEqual(self.high_characters.decode('latin-1'), result)
-
-
-class TestMessageJob(TestCaseWithFactory):
- """Tests for MessageJob."""
-
- layer = LaunchpadFunctionalLayer
-
- def test_providesInterface(self):
- """Ensure that BranchJob implements IBranchJob."""
- # Ensure database constraints are satisfied.
- file_alias = self.factory.makeMergeDirectiveEmail()[1]
- job = MessageJob(file_alias, MessageJobAction.CREATE_MERGE_PROPOSAL)
- job.sync()
- verifyObject(IMessageJob, job)
-
- def test_destroySelf_destroys_job(self):
- """Ensure that MessageJob.destroySelf destroys the Job as well."""
- file_alias = self.factory.makeMergeDirectiveEmail()[1]
- message_job = MessageJob(
- file_alias, MessageJobAction.CREATE_MERGE_PROPOSAL)
- job_id = message_job.job.id
- message_job.destroySelf()
- self.assertRaises(SQLObjectNotFound, Job.get, job_id)
-
- def test_getMessage(self):
- """getMessage should return a Message with appropriate values."""
- ctrl = MailController(
- 'from@xxxxxxxxxxx', ['to@xxxxxxxxxxx'], 'subject', 'body')
- content = ctrl.makeMessage().as_string()
- lfa = getUtility(ILibraryFileAliasSet).create(
- 'message', len(content), StringIO(content), 'text/x-diff')
- message_job = MessageJob(lfa, MessageJobAction.CREATE_MERGE_PROPOSAL)
- transaction.commit()
- message = message_job.getMessage()
- self.assertEqual('from@xxxxxxxxxxx', message['From'])
- self.assertEqual('to@xxxxxxxxxxx', message['To'])
- self.assertEqual('subject', message['Subject'])
- self.assertEqual('body', message.get_payload())
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2012-04-23 20:27:38 +0000
+++ lib/lp/testing/factory.py 2012-04-27 19:10:25 +0000
@@ -45,7 +45,6 @@
from types import InstanceType
import warnings
-from bzrlib.merge_directive import MergeDirective2
from bzrlib.plugins.builder.recipe import BaseRecipeBranch
from bzrlib.revision import Revision as BzrRevision
from lazr.jobrunner.jobrunner import SuspendJobException
@@ -4100,87 +4099,6 @@
msg.attach(attachment)
return msg
- def makeBundleMergeDirectiveEmail(self, source_branch, target_branch,
- signing_context=None, sender=None):
- """Create a merge directive email from two bzr branches.
-
- :param source_branch: The source branch for the merge directive.
- :param target_branch: The target branch for the merge directive.
- :param signing_context: A GPGSigningContext instance containing the
- gpg key to sign with. If None, the message is unsigned. The
- context also contains the password and gpg signing mode.
- :param sender: The `Person` that is sending the email.
- """
- md = MergeDirective2.from_objects(
- source_branch.repository, source_branch.last_revision(),
- public_branch=source_branch.get_public_branch(),
- target_branch=target_branch.getInternalBzrUrl(),
- local_target_branch=target_branch.getInternalBzrUrl(), time=0,
- timezone=0)
- email = None
- if sender is not None:
- email = removeSecurityProxy(sender).preferredemail.email
- return self.makeSignedMessage(
- body='My body', subject='My subject',
- attachment_contents=''.join(md.to_lines()),
- signing_context=signing_context, email_address=email)
-
- def makeMergeDirective(self, source_branch=None, target_branch=None,
- source_branch_url=None, target_branch_url=None):
- """Return a bzr merge directive object.
-
- :param source_branch: The source database branch in the merge
- directive.
- :param target_branch: The target database branch in the merge
- directive.
- :param source_branch_url: The URL of the source for the merge
- directive. Overrides source_branch.
- :param target_branch_url: The URL of the target for the merge
- directive. Overrides target_branch.
- """
- from bzrlib.merge_directive import MergeDirective2
- if source_branch_url is not None:
- assert source_branch is None
- else:
- if source_branch is None:
- source_branch = self.makeAnyBranch()
- source_branch_url = (
- config.codehosting.supermirror_root +
- source_branch.unique_name)
- if target_branch_url is not None:
- assert target_branch is None
- else:
- if target_branch is None:
- target_branch = self.makeAnyBranch()
- target_branch_url = (
- config.codehosting.supermirror_root +
- target_branch.unique_name)
- return MergeDirective2(
- 'revid', 'sha', 0, 0, target_branch_url,
- source_branch=source_branch_url, base_revision_id='base-revid',
- patch='')
-
- def makeMergeDirectiveEmail(self, body='Hi!\n', signing_context=None):
- """Create an email with a merge directive attached.
-
- :param body: The message body to use for the email.
- :param signing_context: A GPGSigningContext instance containing the
- gpg key to sign with. If None, the message is unsigned. The
- context also contains the password and gpg signing mode.
- :return: message, file_alias, source_branch, target_branch
- """
- target_branch = self.makeProductBranch()
- source_branch = self.makeProductBranch(
- product=target_branch.product)
- md = self.makeMergeDirective(source_branch, target_branch)
- message = self.makeSignedMessage(body=body,
- subject='My subject', attachment_contents=''.join(md.to_lines()),
- signing_context=signing_context)
- message_string = message.as_string()
- file_alias = getUtility(ILibraryFileAliasSet).create(
- '*', len(message_string), StringIO(message_string), '*')
- return message, file_alias, source_branch, target_branch
-
def makeHWSubmission(self, date_created=None, submission_key=None,
emailaddress=u'test@xxxxxxxxxxxxx',
distroarchseries=None, private=False,
@@ -4446,7 +4364,6 @@
BaseRecipeBranch,
DSCFile,
InstanceType,
- MergeDirective2,
Message,
datetime,
int,
Follow ups