45
def _signed_submission(branch, config, target_base):
46
"""Get the signed text that we would submit to the pqm"""
48
merge_target = config.get_user_option('pqm_branch')
50
raise errors.BzrCommandError('No PQM target branch specified '
53
unsigned_text = 'star-merge %s %s' % (target_base, merge_target)
54
unsigned_text = unsigned_text.encode('ascii') #URLs should be ascii
56
strategy = bzrlib.gpg.GPGStrategy(config)
58
return strategy.sign(unsigned_text)
61
_default_headers = '''From: %s
68
def _setup_headers(message, user_from, user_to):
69
"""Get all of the important headers filled out."""
71
raise BadCommitMessage(message)
72
return _default_headers % (user_from, user_to, message)
75
def public_branch(branch, config):
76
target_base = config.get_user_option('public_branch')
77
if target_base is None:
78
repo_loc = branch.repository.bzrdir.root_transport.local_abspath('.')
79
repo_config = bzrlib.config.LocationConfig(repo_loc)
80
public_repo = repo_config.get_user_option("public_repository")
81
if public_repo is not None:
82
branch_relpath = bzrlib.osutils.relpath(repo_loc,
83
branch.bzrdir.root_transport.local_abspath('.'))
84
target_base = bzrlib.urlutils.join(public_repo, branch_relpath)
40
class PQMSubmission(object):
41
"""A request to perform a PQM merge into a branch."""
43
def __init__(self, source_branch, public_location=None,
44
submit_location=None, message=None):
45
"""Create a PQMSubmission object.
47
:param source_branch: the source branch for the merge
48
:param public_location: the public location of the source branch
49
:param submit_location: the location of the target branch
50
:param message: The message to use when committing this merge
52
If any of public_location, submit_location or message are
53
omitted, they will be calculated from source_branch.
55
if source_branch is None:
56
raise errors.NoMergeSource()
57
self.source_branch = source_branch
59
if public_location is None:
60
public_location = self.source_branch.get_public_branch()
61
# Fall back to the old public_repository hack.
62
if public_location is None:
63
src_loc = source_branch.bzrdir.root_transport.local_abspath('.')
64
repository = source_branch.repository
65
repo_loc = repository.bzrdir.root_transport.local_abspath('.')
66
repo_config = _mod_config.LocationConfig(repo_loc)
67
public_repo = repo_config.get_user_option("public_repository")
68
if public_repo is not None:
69
warning("Please use public_branch, not public_repository, "
70
"to set the public location of branches.")
71
branch_relpath = osutils.relpath(repo_loc, src_loc)
72
public_location = urlutils.join(public_repo, branch_relpath)
74
if public_location is None:
75
raise errors.BzrCommandError(
76
'No public branch location given. Please specify with '
77
'--public-location or see "bzr help pqm-submit" to see how '
78
'to set it in ~/.bazaar/locations.conf')
79
self.public_location = public_location
81
if submit_location is None:
82
config = self.source_branch.get_config()
83
# First check the deprecated pqm_branch config key:
84
submit_location = config.get_user_option('pqm_branch')
85
if submit_location is not None:
86
warning("Please use submit_branch, not pqm_branch to set "
87
"the PQM merge target branch.")
89
# Otherwise, use the standard config key:
90
submit_location = self.source_branch.get_submit_branch()
92
if submit_location is None:
93
raise errors.NoSubmitBranch(self.source_branch)
94
self.submit_location = submit_location
96
# Check that the message is okay to pass to PQM
98
repository = self.source_branch.repository
99
rev = repository.get_revision(self.source_branch.last_revision())
100
message = rev.message
101
self.message = message.encode('utf8')
102
if '\n' in self.message:
103
raise BadCommitMessage(self.message)
105
def check_public_branch(self):
106
"""Check that the public branch is up to date with the local copy."""
107
note('Checking that the public branch is up to date ...')
108
local_revision = self.source_branch.last_revision()
109
public_revision = Branch.open(self.public_location).last_revision()
110
if local_revision != public_revision:
111
raise errors.PublicBranchOutOfDate(
112
self.public_location, local_revision)
115
"""Serialise as a list of lines."""
116
return ['star-merge %s %s\n' % (self.public_location, self.submit_location)]
119
"""Serialize as a signed string."""
120
unsigned_text = ''.join(self.to_lines())
121
unsigned_text = unsigned_text.encode('ascii') #URLs should be ascii
123
strategy = gpg.GPGStrategy(self.source_branch.get_config())
124
return strategy.sign(unsigned_text)
126
def to_email(self, mail_from, mail_to, sign=True):
127
"""Serialize as an email message.
129
:param mail_from: The from address for the message
130
:param mail_to: The address to send the message to
131
:param sign: If True, gpg-sign the email
132
:return: an email message
135
body = self.to_signed()
137
body = ''.join(self.to_lines())
138
message = EmailMessage(mail_from, mail_to, self.message, body)
88
142
def submit(branch, message, dry_run=False, public_location=None):
89
143
"""Submit the given branch to the pqm."""
90
144
config = branch.get_config()
93
rev = branch.repository.get_revision(branch.last_revision())
95
message = message.encode('utf8')
146
submission = PQMSubmission(
147
source_branch=branch, public_location=public_location, message=message)
97
user_from = config.get_user_option('pqm_user_email')
99
user_from = config.username()
100
user_from = user_from.encode('utf8') # Make sure this isn't unicode
101
user_to = config.get_user_option('pqm_email')
149
mail_from = config.get_user_option('pqm_user_email')
151
mail_from = config.username()
152
mail_from = mail_from.encode('utf8') # Make sure this isn't unicode
153
mail_to = config.get_user_option('pqm_email')
103
155
raise errors.BzrCommandError('No PQM submission address specified '
104
156
'in configuration')
105
user_to = user_to.encode('utf8') # same here
107
# All the entries must be utf-8 so the message creation doesn't fail
108
msg = _setup_headers(message, user_from, user_to)
110
server = config.get_user_option('smtp_server')
112
server = _default_smtp_server
113
smtp_username = config.get_user_option('smtp_username')
114
smtp_password = config.get_user_option('smtp_password')
116
if public_location is None:
117
target_base = public_branch(branch, config)
119
target_base = public_location
121
if target_base is None:
122
raise errors.BzrCommandError('Could not find public location, either'
123
'specify with --public-location, or see'
124
' "bzr help pqm-submit" to see how to set'
125
'it in ~/.bazaar/locations.conf')
127
note('Checking that the public branch is up to date ...')
129
public_revision = Branch.open(target_base).last_revision()
130
except errors.NotBranchError:
131
raise errors.BzrCommandError(
132
'Public location has no branch: %s' % target_base)
133
if public_revision != branch.last_revision():
134
raise errors.BzrCommandError(
135
'Local last revision does not match public last revision: %s' %
137
# Get the signed text, this should be done just before
138
# emailing, so that we don't run gpg --cl for nothing
139
msg += _signed_submission(branch, config, target_base)
157
mail_to = mail_to.encode('utf8') # same here
159
submission.check_public_branch()
161
message = submission.to_email(mail_from, mail_to)
142
return print_info(msg, server, user_from, user_to)
144
smtp = smtplib.SMTP()
147
# The world can always use a little bit more encryption :)
150
if smtp_username is not None:
151
if smtp_password is None:
152
smtp_password = bzrlib.ui.ui_factory.get_password(
153
'Please enter SMTP password for %(host)s', host=server)
155
smtp.login(smtp_username, smtp_password)
156
except smtplib.SMTPHeloError, e:
157
raise errors.BzrCommandError('SMTP server refused HELO: %d %s'
158
% (e.smtp_code, e.smtp_error))
159
except smtplib.SMTPAuthenticationError, e:
160
raise errors.BzrCommandError('SMTP server refused authentication: %d %s'
161
% (e.smtp_code, e.smtp_error))
162
except smtplib.SMTPException, e:
163
raise errors.BzrCommandError(str(e))
165
smtp.sendmail(user_from, [user_to], msg)
168
def print_info(msg, server, user_from, user_to):
169
print "Server: %s" % server
164
print message.as_string()
167
SMTPConnection(config).send_email(message)