This is my first attempt a for git command I can use to push the latest commit and email the diff. Story behind how it came up: http://goo.gl/3NEHvn
Revision 1 (Scroll down for 2nd Revision)
import argparse
import subprocess
import os
import re
import send
import sys
class INVALIDEMAIL(Exception):
pass
def main():
parser = argparse.ArgumentParser(prog='ipush', description='Utility to push the last commit and email the color diff')
parser.add_argument('-v', '--verbose', action='store_true', help='if enabled will spit every command and its resulting data.')
parser.add_argument('-c', '--compose', help='text message to be sent with diff')
parser.add_argument('-to', type=str, help='enter a valid email you want to send to.')
parser.add_argument('-ver', '--version', action='version', version='%(prog)s 1.0')
parser.add_argument('-d', '--diff', required=False, help='if present pass arguments to it as you will do to git diff in inverted commas')
args = parser.parse_args()
VERBOSE = args.verbose
optArgs = vars(args)
if optArgs['to']:
sendTo = optArgs['to']
pattern = "^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$"
if not re.match(pattern, sendTo) != None:
raise INVALIDEMAIL("You have entered an invalid email to send to.")
else:
sendto = "[email protected]"
diffCmd = 'git diff HEAD^ HEAD' if not optArgs['diff'] else "git diff %s" % optArgs['diff']
branchName, _ = execGitCommand('git rev-parse --abbrev-ref HEAD')
# stripping newline character which got appended when pulling branch name
branchName = branchName.split("\n")[0]
commitComment, _ = execGitCommand('git log -1 --pretty=%B')
subject = "%s: %s" % (branchName, commitComment)
# check for fatal error when executing git command
diffData, error = execGitCommand(diffCmd, VERBOSE)
if 'fatal' not in error.split(":"):
modifiedData, error = execGitCommand('git status', VERBOSE)
if any([re.search(word, modifiedData) for word in ['modified', 'untracked']]):
print "\x1b[31m You have uncommited changes, Commit and try again:\x1b[m"
return
# only push that is displayed
if diffCmd in ['git diff HEAD^ HEAD', 'git diff HEAD~ HEAD']:
pushBranch(VERBOSE)
if diffData:
htmlDiff = getHtml(diffData.split("\n"))
message = htmlDiff if not optArgs['compose'] else "%s<br><br>%s" % (
optArgs['compose'], htmlDiff)
emailDiff(subject, sendto, message)
else:
print error.capitalize()
def getHtml(diffData):
""" This method convertes git diff data to html color code
"""
lines = ""
openTag = "<span style='font-size: .80em; color: "
openTagEnd = ";font-family: courier, arial, helvetica, sans-serif;'>"
nbsp = ' '
for eachLine in diffData:
if eachLine.startswith('-'):
tabs = eachLine.count('\t')
lines += "%s#ff0000%s%s%s</span><br>" % (openTag, openTagEnd, nbsp*tabs ,eachLine)
elif eachLine.startswith('+'):
tabs = eachLine.count('\t')
lines += "%s#007900%s%s%s</span><br>" % (openTag, openTagEnd, nbsp*tabs, eachLine)
else:
tabs = eachLine.count('\t')
lines += "%s#000000%s%s%s</span><br>" % (openTag, openTagEnd, nbsp*tabs, eachLine)
return lines
def execGitCommand(command=None, verbose=False):
""" Function used to get data out of git commads
and errors in case of failure.
Args:
command(string): string of a git command
verbose(bool): whether to display every command
and its resulting data.
Return:
(tuple): string of Data and error if present
"""
if command:
pr = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
msg = pr.stdout.read()
err = pr.stderr.read()
txt = "\x1b[32mResult:\x1b[m\n"+msg if msg else "\x1b[31mError:\x1b[m\n"+err
if verbose:
print "Executing '%s'\n%s" % (command, txt)
return msg, err
def emailDiff(subject, emailTo, htmlDiff):
""" This function send color diff via email
Args:
subject(string): name of the branch with commit message
htmlDiff(string): html formatted string
"""
mail = send.EMail(
mailFrom=os.getenv('myemail'),
server='smtp.gmail.com',
usrname=os.getenv('myemail').split('@')[0],
password=os.getenv('PASS'),
files=None,
debug=False
)
mail.sendMessage(subject, htmlDiff, None, emailTo)
print "\x1b[31m Diff of branch, %s sent to email: %s .\x1b[m" % (subject, emailTo)
def pushBranch(VERBOSE):
""" Pushes the branch to remote repository
Args:
VERBOSE(bool): defines whether to spit out which command
is being executed and result of command.
Return:
(bool) True if push Succesfull else False
"""
_, err = execGitCommand('git push')
if re.search(r'rejected', err):
print '%s\n\x1b[31mDo you want to try force push?\x1b[m\n\x1b[31mEnter yes to force Push else any key to cancel\x1b[m' % err
# default value yes and hit enter without entering anything
answer = raw_input('[YES]')
if answer and answer.lower() not in ['yes', 'y', 'f', 'git push -f']:
print "Cancelled !!!"
else:
execGitCommand('git push -f', VERBOSE)
print "\x1b[33mPush Succesfull !\x1b[m" if VERBOSE else ""
return True
else:
print "\x1b[33mPush Succesfull !\x1b[m" if VERBOSE else ""
return True
return False
if __name__ == '__main__':
sys.exit(main())
It has dependency file that sends the email [send.py]
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
import smtplib
class EMail(object):
""" Class defines method to send email
"""
def __init__(self, mailFrom, server, usrname, password, files, debug=False):
self.debug = debug
self.mailFrom = mailFrom
self.smtpserver = server
self.EMAIL_PORT = 587
self.usrname = usrname
self.password = password
def sendMessage(self, subject, msgContent, files, mailto):
""" Send the email message
Args:
subject(string): subject for the email
msgContent(string): email message Content
files(List): list of files to be attached
mailto(string): email address to be sent to
"""
msg = self.prepareMail(subject, msgContent, files, mailto)
# connect to server and send email
server=smtplib.SMTP(self.smtpserver, port=self.EMAIL_PORT)
server.ehlo()
# use encrypted SSL mode
server.starttls()
# to make starttls work
server.ehlo()
server.login(self.usrname, self.password)
server.set_debuglevel(self.debug)
try:
failed = server.sendmail(self.mailFrom, mailto, msg.as_string())
except Exception as er:
print er
finally:
server.quit()
def prepareMail(self, subject, msgHTML, attachments, mailto):
""" Prepare the email to send
Args:
subject(string): subject of the email.
msgHTML(string): HTML formatted email message Content.
attachments(List): list of file paths to be attached with email.
"""
msg = MIMEMultipart()
msg['From'] = self.mailFrom
msg['To'] = mailto
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
#the Body message
msg.attach(MIMEText(msgHTML, 'html'))
msg.attach(MIMEText("Sent using git ipush\n git clone https://[email protected]/sanfx/git-ipush.git"))
if attachments:
for phile in attachments:
# we could check for MIMETypes here
part = MIMEBase('application',"octet-stream")
part.set_payload(open(phile, "rb").read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(phile))
msg.attach(part)
return msg
I would really appreciate if you can help me make it more better. How neat is this first attempt?
Revision 2: I have updated the following methods:
def getHtml(diffData):
""" This method convertes git diff data to html color code
Args:
diffData(sting): diff between commits in simple text
"""
openTag = "<span style='font-size: .80em; color: "
openTagEnd = "00;font-family: courier, arial, helvetica, sans-serif;'>"
nbsp = ' '
lines = []
for line in diffData:
color = "#ff00" if line.startswith('-') else ('#0079' if line.startswith('+') else '#0000')
tabs = line.count('\t')
lines.append("%s%s%s%s%s</span><br>" %
((openTag, color, openTagEnd, nbsp*tabs ,line)))
return ''.join(lines)
and this too:
def validate_address(address):
""" If address looks like a valid e-mail address, return it. Otherwise
raise ArgumentTypeError.
Args:
address(string): email address to send to
"""
if re.match('^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$', address):
return address
raise argparse.ArgumentTypeError('Invalid e-mail address: %s' % address)
user@domain
or"Jon Doe"@example.com
. Admittedly not extremely likely (except maybe for the first one). It's much more likely the user has a typo in the address - in which case it's still valid just the wrong address. \$\endgroup\$[email protected]
) \$\endgroup\$