aboutsummaryrefslogtreecommitdiff
path: root/b4/ty.py
diff options
context:
space:
mode:
Diffstat (limited to 'b4/ty.py')
-rw-r--r--b4/ty.py186
1 files changed, 111 insertions, 75 deletions
diff --git a/b4/ty.py b/b4/ty.py
index c6d13cb..2bb7d2f 100644
--- a/b4/ty.py
+++ b/b4/ty.py
@@ -7,10 +7,12 @@ __author__ = 'Konstantin Ryabitsev <konstantin@linuxfoundation.org>'
import os
import sys
+
import b4
import re
import email
import email.message
+import email.policy
import json
from string import Template
@@ -72,28 +74,27 @@ def git_get_commit_message(gitdir, rev):
return b4.git_run_command(gitdir, args)
-def make_reply(reply_template, jsondata):
+def make_reply(reply_template, jsondata, gitdir):
body = Template(reply_template).safe_substitute(jsondata)
# Conform to email standards
body = body.replace('\n', '\r\n')
msg = email.message_from_string(body)
msg['From'] = '%s <%s>' % (jsondata['myname'], jsondata['myemail'])
- allto = utils.getaddresses([jsondata['to']])
- allcc = utils.getaddresses([jsondata['cc']])
- # Remove ourselves and original sender from allto or allcc
- for entry in list(allto):
- if entry[1] == jsondata['myemail'] or entry[1] == jsondata['fromemail']:
- allto.remove(entry)
- for entry in list(allcc):
- if entry[1] == jsondata['myemail'] or entry[1] == jsondata['fromemail']:
- allcc.remove(entry)
-
- # Add original sender to the To
- allto.append((jsondata['fromname'], jsondata['fromemail']))
-
- msg['To'] = b4.format_addrs(allto)
+ excludes = b4.get_excluded_addrs()
+ newto = b4.cleanup_email_addrs([(jsondata['fromname'], jsondata['fromemail'])], excludes, gitdir)
+
+ # Exclude ourselves and original sender from allto or allcc
+ excludes.add(jsondata['myemail'])
+ excludes.add(jsondata['fromemail'])
+ allto = b4.cleanup_email_addrs(utils.getaddresses([jsondata['to']]), excludes, gitdir)
+ allcc = b4.cleanup_email_addrs(utils.getaddresses([jsondata['cc']]), excludes, gitdir)
+
+ if newto:
+ allto += newto
+
+ msg.add_header('To', b4.format_addrs(allto))
if allcc:
- msg['Cc'] = b4.format_addrs(allcc)
+ msg.add_header('Cc', b4.format_addrs(allcc))
msg['In-Reply-To'] = '<%s>' % jsondata['msgid']
if len(jsondata['references']):
msg['References'] = '%s <%s>' % (jsondata['references'], jsondata['msgid'])
@@ -102,9 +103,9 @@ def make_reply(reply_template, jsondata):
subject = re.sub(r'^Re:\s+', '', jsondata['subject'], flags=re.I)
if jsondata.get('cherrypick'):
- msg['Subject'] = 'Re: (subset) ' + subject
+ msg.add_header('Subject', 'Re: (subset) ' + subject)
else:
- msg['Subject'] = 'Re: ' + subject
+ msg.add_header('Subject', 'Re: ' + subject)
mydomain = jsondata['myemail'].split('@')[1]
msg['Message-Id'] = email.utils.make_msgid(idstring='b4-ty', domain=mydomain)
@@ -228,21 +229,6 @@ def auto_locate_series(gitdir, jsondata, branch, since='1.week'):
return found
-def read_template(tptfile):
- # bubbles up FileNotFound
- tpt = ''
- if tptfile.find('~') >= 0:
- tptfile = os.path.expanduser(tptfile)
- if tptfile.find('$') >= 0:
- tptfile = os.path.expandvars(tptfile)
- with open(tptfile, 'r', encoding='utf-8') as fh:
- for line in fh:
- if len(line) and line[0] == '#':
- continue
- tpt += line
- return tpt
-
-
def set_branch_details(gitdir, branch, jsondata, config):
binfo = get_branch_info(gitdir, branch)
jsondata['branch'] = branch
@@ -282,7 +268,7 @@ def generate_pr_thanks(gitdir, jsondata, branch):
if config['thanks-pr-template']:
# Try to load this template instead
try:
- thanks_template = read_template(config['thanks-pr-template'])
+ thanks_template = b4.read_template(config['thanks-pr-template'])
except FileNotFoundError:
logger.critical('ERROR: thanks-pr-template says to use %s, but it does not exist',
config['thanks-pr-template'])
@@ -300,7 +286,7 @@ def generate_pr_thanks(gitdir, jsondata, branch):
if not cidmask:
cidmask = 'merge commit: %s'
jsondata['summary'] = cidmask % jsondata['merge_commit_id']
- msg = make_reply(thanks_template, jsondata)
+ msg = make_reply(thanks_template, jsondata, gitdir)
return msg
@@ -311,7 +297,7 @@ def generate_am_thanks(gitdir, jsondata, branch, since):
if config['thanks-am-template']:
# Try to load this template instead
try:
- thanks_template = read_template(config['thanks-am-template'])
+ thanks_template = b4.read_template(config['thanks-am-template'])
except FileNotFoundError:
logger.critical('ERROR: thanks-am-template says to use %s, but it does not exist',
config['thanks-am-template'])
@@ -348,7 +334,7 @@ def generate_am_thanks(gitdir, jsondata, branch, since):
nomatch, len(commits), jsondata['subject'])
logger.critical(' Please review the resulting message')
- msg = make_reply(thanks_template, jsondata)
+ msg = make_reply(thanks_template, jsondata, gitdir)
return msg
@@ -389,35 +375,38 @@ def auto_thankanator(cmdargs):
sys.exit(0)
logger.info('---')
- send_messages(applied, cmdargs.gitdir, cmdargs.outdir, wantbranch, since=cmdargs.since)
+ send_messages(applied, wantbranch, cmdargs)
sys.exit(0)
-def send_messages(listing, gitdir, outdir, branch, since='1.week'):
- # Not really sending, but writing them out to be sent on your own
- # We'll probably gain ability to send these once the feature is
- # more mature and we're less likely to mess things up
- datadir = b4.get_data_dir()
+def send_messages(listing, branch, cmdargs):
logger.info('Generating %s thank-you letters', len(listing))
- # Check if the outdir exists and if it has any .thanks files in it
- if not os.path.exists(outdir):
- os.mkdir(outdir)
+ gitdir = cmdargs.gitdir
+ datadir = b4.get_data_dir()
+ fromaddr = None
+ smtp = None
+ if cmdargs.sendemail:
+ # See if we have sendemail-identity set
+ config = b4.get_main_config()
+ identity = config.get('sendemail-identity')
+ try:
+ smtp, fromaddr = b4.get_smtp(identity)
+ except Exception as ex: # noqa
+ logger.critical('Failed to configure the smtp connection:')
+ logger.critical(ex)
+ sys.exit(1)
+ else:
+ # We write .thanks notes
+ # Check if the outdir exists and if it has any .thanks files in it
+ if not os.path.exists(cmdargs.outdir):
+ os.mkdir(cmdargs.outdir)
usercfg = b4.get_user_config()
- # Do we have a .signature file?
- sigfile = os.path.join(str(Path.home()), '.signature')
- if os.path.exists(sigfile):
- with open(sigfile, 'r', encoding='utf-8') as fh:
- signature = fh.read()
- else:
- signature = '%s <%s>' % (usercfg['name'], usercfg['email'])
+ signature = b4.get_email_signature()
outgoing = 0
+ msgids = list()
for jsondata in listing:
- slug_from = re.sub(r'\W', '_', jsondata['fromemail'])
- slug_subj = re.sub(r'\W', '_', jsondata['subject'])
- slug = '%s_%s' % (slug_from.lower(), slug_subj.lower())
- slug = re.sub(r'_+', '_', slug)
jsondata['myname'] = usercfg['name']
jsondata['myemail'] = usercfg['email']
jsondata['signature'] = signature
@@ -426,29 +415,65 @@ def send_messages(listing, gitdir, outdir, branch, since='1.week'):
msg = generate_pr_thanks(gitdir, jsondata, branch)
else:
# This is a patch series
- msg = generate_am_thanks(gitdir, jsondata, branch, since)
+ msg = generate_am_thanks(gitdir, jsondata, branch, cmdargs.since)
if msg is None:
continue
+ msgids.append(jsondata['msgid'])
+ for pdata in jsondata.get('patches', list()):
+ msgids.append(pdata[2])
+
outgoing += 1
- outfile = os.path.join(outdir, '%s.thanks' % slug)
- logger.info(' Writing: %s', outfile)
msg.set_charset('utf-8')
msg.replace_header('Content-Transfer-Encoding', '8bit')
- with open(outfile, 'w') as fh:
- fh.write(msg.as_string(policy=b4.emlpolicy))
- logger.debug('Cleaning up: %s', jsondata['trackfile'])
- fullpath = os.path.join(datadir, jsondata['trackfile'])
- os.rename(fullpath, '%s.sent' % fullpath)
+ if cmdargs.sendemail:
+ if not fromaddr:
+ fromaddr = jsondata['myemail']
+ logger.info(' Sending: %s', msg.get('subject'))
+ # We never want to use the web endpoint for this (it's only for submitting patches)
+ b4.send_mail(smtp, [msg], fromaddr, dryrun=cmdargs.dryrun, use_web_endpoint=False)
+ else:
+ slug_from = re.sub(r'\W', '_', jsondata['fromemail'])
+ slug_subj = re.sub(r'\W', '_', jsondata['subject'])
+ slug = '%s_%s' % (slug_from.lower(), slug_subj.lower())
+ slug = re.sub(r'_+', '_', slug)
+ outfile = os.path.join(cmdargs.outdir, '%s.thanks' % slug)
+ logger.info(' Writing: %s', outfile)
+ with open(outfile, 'w') as fh:
+ fh.write(msg.as_string(policy=b4.emlpolicy))
+ if cmdargs.dryrun:
+ logger.info('Dry run, preserving tracked series.')
+ else:
+ logger.debug('Cleaning up: %s', jsondata['trackfile'])
+ fullpath = os.path.join(datadir, jsondata['trackfile'])
+ os.rename(fullpath, '%s.sent' % fullpath)
+
logger.info('---')
if not outgoing:
logger.info('No thanks necessary.')
return
- logger.debug('Wrote %s thank-you letters', outgoing)
- logger.info('You can now run:')
- logger.info(' git send-email %s/*.thanks', outdir)
+ config = b4.get_main_config()
+ pwstate = cmdargs.pw_set_state
+ if not pwstate:
+ pwstate = config.get('pw-accept-state')
+
+ if cmdargs.sendemail:
+ if cmdargs.dryrun:
+ logger.info('DRYRUN: generated %s thank-you letters', outgoing)
+ else:
+ logger.info('Sent %s thank-you letters', outgoing)
+ if pwstate:
+ b4.patchwork_set_state(msgids, pwstate)
+ smtp.quit()
+ else:
+ if pwstate:
+ b4.patchwork_set_state(msgids, pwstate)
+ logger.info('---')
+ logger.debug('Wrote %s thank-you letters', outgoing)
+ logger.info('You can now run:')
+ logger.info(' git send-email %s/*.thanks', cmdargs.outdir)
def list_tracked():
@@ -480,17 +505,17 @@ def write_tracked(tracked):
counter += 1
-def send_selected(cmdargs):
+def thank_selected(cmdargs):
tracked = list_tracked()
if not len(tracked):
logger.info('Nothing to do')
sys.exit(0)
- if cmdargs.send == 'all':
+ if cmdargs.thankfor == 'all':
listing = tracked
else:
listing = list()
- for num in b4.parse_int_range(cmdargs.send, upper=len(tracked)):
+ for num in b4.parse_int_range(cmdargs.thankfor, upper=len(tracked)):
try:
index = int(num) - 1
listing.append(tracked[index])
@@ -509,7 +534,7 @@ def send_selected(cmdargs):
sys.exit(0)
wantbranch = get_wanted_branch(cmdargs)
- send_messages(listing, cmdargs.gitdir, cmdargs.outdir, wantbranch, cmdargs.since)
+ send_messages(listing, wantbranch, cmdargs)
sys.exit(0)
@@ -544,10 +569,21 @@ def discard_selected(cmdargs):
datadir = b4.get_data_dir()
logger.info('Discarding %s messages', len(listing))
+ msgids = list()
for jsondata in listing:
fullpath = os.path.join(datadir, jsondata['trackfile'])
os.rename(fullpath, '%s.discarded' % fullpath)
logger.info(' Discarded: %s', jsondata['subject'])
+ msgids.append(jsondata['msgid'])
+ for pdata in jsondata.get('patches', list()):
+ msgids.append(pdata[2])
+
+ config = b4.get_main_config()
+ pwstate = cmdargs.pw_set_state
+ if not pwstate:
+ pwstate = config.get('pw-discard-state')
+ if pwstate:
+ b4.patchwork_set_state(msgids, pwstate)
sys.exit(0)
@@ -636,9 +672,9 @@ def main(cmdargs):
if cmdargs.auto:
check_stale_thanks(cmdargs.outdir)
auto_thankanator(cmdargs)
- elif cmdargs.send:
+ elif cmdargs.thankfor:
check_stale_thanks(cmdargs.outdir)
- send_selected(cmdargs)
+ thank_selected(cmdargs)
elif cmdargs.discard:
discard_selected(cmdargs)
else:
@@ -649,4 +685,4 @@ def main(cmdargs):
write_tracked(tracked)
logger.info('---')
logger.info('You can send them using number ranges, e.g:')
- logger.info(' b4 ty -s 1-3,5,7-')
+ logger.info(' b4 ty -t 1-3,5,7-')