Python: Git Log自动生成Release Notes,并调用Outlook发送至邮件
主要功能
主要功能是 :在两个软件版本号之间,对Git Log进行过滤,筛选出所需要的Log(比如ReleaseNotes中需要的已解决Issue、实现的Feature等),将相关内容生成Text版本的ReleaseNotes文件,并自动发送至Email (Outlook方式)
其中UI是通过Tkinter 做版本号输入框和Button进行交互
先上图
生成ReleaseNotes 邮件:
生成ReleaseNotes Text:
分别有版本号、Issue/Feature、Generated Info等相关内容:
关键代码:
Git Log筛选
直接使用的Git Log命令+ Grep 获取相关命名规则的log:
(Git Log命令使用参考: https://git-scm.com/docs/git-log )
首先需要优化下Git log显示的内容和格式
对log进行格式化显示,只提取显示需要的内容,
例如 我只想显示Commit Hash/时间/作者信息,并且每条都显示为一行,则只需下面命令,就会指定显示format,包括显示的日期格式,Log内容等:
git log --no-merges --pretty=oneline --format="%H, %ad, %s" --date=format:"%Y-%m-%d %H:%M"
其中pretty format选项常用的有:
- %s: commit log
- %H: commit的完整哈希值
- %h: commit的简化哈希值(前9Byte)
- %an: author Name, 作者名字
- %ad: Date, 既作者修改的日期
比如上面命令的%H 改为%h,则会显示简化hash:
同时可以在上面log里 ,再使用 Grep 按关键字过滤出任意需要的log,实现筛选
getCommitDate_byFwVer 获取版本号对应的log hash
基于上面的log筛选, 通过getCommitDate_byFwVer(fwVersion) 根据输入的版本号,获取log中出版本号对应的 hash、date
因为前面排列已经是固定按hash、date,所以这里直接截取字符串位置,就可以得到commitHash 和commitDate
def getCommitDate_byFwVer(fwVersion):
cmd = "git log --no-merges --pretty=oneline --format=\"%H, %ad, %s\" --date=format:\"%Y-%m-%d %H:%M\" | grep \"Version: " + fwVersion + "\""
ret = executeGitCmd(cmd)
print(ret)
commitHash = ret[0:40]
commitDate = ret[42:58]
return commitHash, commitDate
如下面示例,即可过滤出版本号所对应的Log:
getFilterLog_byHash
getFilterLog_byHash() 通过上一步获取的版本号对应的LogHash,筛选出两个版本号之间所需要的 Log内容以及提交信息
(其中 %an 是提交作者,%ad是提交日期)
例如下面示例:
def getFilterLog_byHash(Hash_Start, Hash_End, KEYWORD):
GREP_STR = " | grep -i -E \"" + KEYWORD + "\""
cmd = "git log --no-merges --pretty=oneline --format=\"\t* %s - (by %an, %ad)\" --date=short " + Hash_Start + "..." + Hash_End + GREP_STR
ret = executeGitCmd(cmd)
return ret
输出ReleaseNotes到Text
根据以上的Log,将获取到的log、Hash、Date等,输出到Text文件里,这一步比较常规,不多介绍了
print(">>>Writing to "+ FILE_RLSNOTES_NAME)
with open(FILE_RLSNOTES_PATH, 'w+') as file:
file.write('New Release for Howard CSDN Blog Demo as below: \n\n')
file.write(f'- Software Version: {SW_VER_END}\n')
file.write(f'- Application Version: {VER_APPLICATION}\n')
file.write(f'- Bootloader Version: {VER_BOOTLOADER}\n\n')
file.write('Release Notes:\n')
file.write(' 1.Resolved Issues:\n')
file.write(commits_Defect)
file.write('\n')
file.write(' 2.Added Feature/User Story:\n')
file.write(commits_Story)
file.write('\n\nGenerated Info:\n')
file.write(f'--Hash from: {Hash_Start} to: {Hash_End} \n')
file.write(f'--Date from: {Date_Start} to: {Date_End} \n')
file.write(f'--Version from: {SW_VER_START} to: {SW_VER_END} \n')
file.write(f'--Generate Date: {str(datetime.date.today())} \n\n')
file.write('//Auto Generated by Release Notes Tools from HowardXue.\n')
输出ReleaseNotes到Email
在ReleaseNotes文件生成后,开始自动生成并发送Email:
通过读取Text文件,使用win32.Dispatch(‘outlook.application’) 调用Outlook,然后填充Email的内容:
mail相关调用的API:
- mail.To 填写发送方
- mail.Subject填写邮件主题
- mail.HTMLBody 填充HTML格式 的ReleaseNotes内容,如相关版本号、标题、内容详情等
- mail.Attachments.Add() 添加附件text
- mail.display() 显示待发送的邮件
- mail.send() 发送邮件
其中显示内容通过 body_Data = '<pre>'+ rlsNotesContent +'</pre>'
,就可以在Html格式中Body显示前面生成的Txt文件内容
def autoEmailOutlook():
global FILE_RLSNOTES_PATH
with open(FILE_RLSNOTES_PATH) as record:
rlsNotesContent = record.read()
print(">>>Generating Email content.... ")
msgStyle = """ <style>
pre {
font:12pt Times New Roman;
width: 900px;
padding: .5rem;
border: 2px solid #d0e579;
margin: 1rem auto;
white-space: pre-wrap;
word-wrap: break-word;
background-color:#f2e7ca;
}
</style>"""
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = MAIL_SEND_TO
global SW_VER_END
mail.Subject = MAIL_SUBJECT + SW_VER_END
titleTag = '<h2>New Software Release of : ' + SW_VER_END + '</h2> '
wikiUrlTag = '<h4><a href=' + BLOG_PAGE_PATH + '>Blog Link: https://blog.csdn.net/HowieXue </a></h4>'
svnUrlTag = '<h4><a href=' + DOWNLOAD_PAGE_PATH + '>Download: Link of Package </a></h4>'
header_Data = titleTag + wikiUrlTag + '\n' + svnUrlTag + '\n'
body_Data = '<pre>'+ rlsNotesContent +'</pre>'
mail.HTMLBody = msgStyle + header_Data + body_Data
print(mail.HTMLBody)
# To attach a file to the email (optional):
attachment = FILE_RLSNOTES_PATH
mail.Attachments.Add(attachment)
mail.display()
gui_Display() 显示
和之前的blog一样,也是使用Tkinter lib增加一些显示Button和文本输入框,方便输入版本号点击Button 交互使用:
然后因为执行需要些时间,所以加了个滑动条显示进度。。。
def gui_Display():
global root
root = Tk()
root.geometry('600x300')
root.title('Auto Release Notes Generator v1.0 for Blog Usage (contact: Howard Xue)')
root.config(bg='#f0ffff')
intro_FwVersion = Label(root, text='Generate SW Release Notes between version: Start -> Target', anchor="w", font=("Calibri 13"), \
bg='#f0ffff', \
fg='blue', \
width=30, \
height=5)
intro_FwVersion.place(relx=0.1, rely=0.13, relwidth=0.8, relheight=0.1)
# Entry
global Et_FwVersion
Et_FwVersion = Entry(root, bd=2, bg="white", relief=GROOVE, font=("Calibri 13"))
Et_FwVersion.place(relx=0.47, rely=0.33, relwidth=0.21, relheight=0.1)
Et_FwVersion.bind('<Return>', OnSoftwareVersionModified)
# Et_FwVersion.bind('<Leave>', OnSoftwareVersionModified)
# Et_FwVersion.bind('<FocusOut>', OnSoftwareVersionModified)
Et_FwVersion.insert(0,SW_VER_END)
intro_FwVersion = Label(root, text='Target Software Version:', anchor="w", font=("Calibri 13"),\
bg='#f0ffff', \
fg='black', \
width=30, \
height=5)
intro_FwVersion.place(relx=0.14, rely=0.33, relwidth=0.32, relheight=0.1)
# Entry
global Et_FwVersion_Start
Et_FwVersion_Start = Entry(root, bd=2, bg="white", relief=GROOVE, font=("Calibri 13"))
Et_FwVersion_Start.place(relx=0.47, rely=0.53, relwidth=0.21, relheight=0.1)
Et_FwVersion_Start.bind('<Return>', OnStartSoftwareVersionModified)
# Et_FwVersion_Start.bind('<Leave>', OnStartSoftwareVersionModified)
# Et_FwVersion.bind('<FocusOut>', OnSoftwareVersionModified)
Et_FwVersion_Start.insert(0, SW_VER_START)
intro_FwVersion_Start = Label(root, text='Start Software Version:', anchor="w", font=("Calibri 13"), \
bg='#f0ffff', \
fg='black', \
width=30, \
height=5)
intro_FwVersion_Start.place(relx=0.14, rely=0.53, relwidth=0.32, relheight=0.1)
btn_Generate = Button(root, text="Generate", command=GenerateBtn, font=("Calibri 13") ,\
fg='black', \
width=20, \
height=5)
btn_Generate.place(relx=0.35, rely=0.71, relwidth=0.2, relheight=0.15)
global p1
p1 = ttk.Progressbar(root, length=120, mode="determinate", maximum=100, orient=HORIZONTAL)
p1.grid(row=1, column=1)
p1.place(relx=0.35, rely=0.89)
root.mainloop()
版本号格式检查(正则)
因为版本号有格式要求,所以增加一个版本号检查,避免误操作:输入版本号后,如果不满足 0.1.2.3 这种数字格式,就会报错:
实现简单用re.match():
def OnStartSoftwareVersionModified(*args):
global SW_VER_START, Et_FwVersion
Version_Input = Et_FwVersion_Start.get()
if Version_Input == "":
return False
# pattern = r'[0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9]\Z'
pattern = r'\d+.\d+.\d+.\d+\Z'
result = re.match(pattern, Version_Input)
# print(result)
if result == None:
tkinter.messagebox.showerror('Error: Start Software Version', 'Please Input Correct Version, eg: 0.1.2.3')
return False
else:
SW_VER_START = Version_Input
print("Last FV Start Input:" + SW_VER_START)
return True
博主热门文章推荐:
一篇读懂系列:
LoRa Mesh系列:
网络安全系列:
- ATECC508A芯片开发笔记(一):初识加密芯片
- SHA/HMAC/AES-CBC/CTR 算法执行效率及RAM消耗 测试结果
- 常见加密/签名/哈希算法性能比较 (多平台 AES/DES, DH, ECDSA, RSA等)
- AES加解密效率测试(纯软件AES128/256)–以嵌入式Cortex-M0与M3 平台为例
嵌入式开发系列:
- 嵌入式学习中较好的练手项目和课题整理(附代码资料、学习视频和嵌入式学习规划)
- IAR调试使用技巧汇总:数据断点、CallStack、设置堆栈、查看栈使用和栈深度、Memory、Set Next Statement等
- Linux内核编译配置(Menuconfig)、制作文件系统 详细步骤
- Android底层调用C代码(JNI实现)
- 树莓派到手第一步:上电启动、安装中文字体、虚拟键盘、开启SSH等
- Android/Linux设备有线&无线 双网共存(同时上内、外网)
AI / 机器学习系列:
- AI: 机器学习必须懂的几个术语:Lable、Feature、Model…
- AI:卷积神经网络CNN 解决过拟合的方法 (Overcome Overfitting)
- AI: 什么是机器学习的数据清洗(Data Cleaning)
- AI: 机器学习的模型是如何训练的?(在试错中学习)
- 数据可视化:TensorboardX安装及使用(安装测试+实例演示)