前言

  • 项目越来越多,版本管理越来越麻烦,在项目上我使用 maven version 来进行版本管理。主要还是在分布式项目中模块众多的场景中使用,毕竟各个模块对外的版本需要保持统一。

  • 关于这个插件如何使用呢?也是非常的简单。只需要在maven视图中进行设置版本号即可将分模块项目的版本进行升级了。

  • 除了idea插件外,maven本身也提供了一个版本管理工具 versions-maven-plugin 。 具体用法以后有机会在赘述。

自定义实现版本更新

  • 作为一个专业懒人,我还是觉得idea的插件不够智能,确切的说还不够自动化。之前我已经动手实现了防 jenkins 自动打包上传启动服务的脚本的功能了,难道提交合并代码这种简单的事情还需要我自己处理吗。不得不承认代码冲突了的确还是需要认为干涉的,但是在平时开发中有多少概率会发生代码冲突呢?我们都是分工合作基本上代码冲突概率很低。
  • 关于代码提交,自己分支如何合并到dev, 如何保证自己分支代码最新等等这些场景我们只需要通过脚本来进行自动化操作就行了。针对这些功能场景我大概写了两个脚本 batfhmerge.shbatchgrade.sh 就搞定了。
  • 然而我想说的是关于项目的版本如何升级。上面也提到了我们分工合作就必然涉及到别人使用你的jar的场景了。你可以使用 SNAPSHOT 版本来保证别人拉取到你最新的功能代码,但是有些公司会要求使用非 SNAPSHOT 版本进行管理也就是正式版本,这样做的好处就是容易找到之前的版本代码功能。

SHELL 实现

  • 之前用SHELL 实现了自动更新置顶项目的版本号为最新日期后缀。虽然使用起来没发现有什么BUG, 但是感觉代码实现上还是很弱智的。
# 该脚本主要用来升级发包期间修改各服务版本
FILEPATH=$1
GROUPID=$2
ARTIFACTID=$3
FILENAME=$4
while getopts ":f:g:a:" opt
docase $opt inf)FILENAME=$OPTARGecho "您输入的文件配置:$FILENAME";;g)GROUPID=$OPTARGecho "您输入的groupid配置:$GROUPID";;a)ARTIFACTID=$OPTARGecho "您输入的artifactid配置:$ARTIFACTID";;ff)FILENAME=$OPTARGecho "您输入的带修改文件为:$FILENAME";;?)echo "未知参数"exit 1;;esac
done
echo "开始修改版本号"
NEWCONTENT=1.2.5.$(date +%Y%m%d)
LINE=`cat ${FILENAME} | grep -n -A 1 '<groupId>'"${GROUPID}"'<\/groupId>'| grep -n '<artifactId>'"${ARTIFACTID}"'<\/artifactId>' | awk -F "[:-]+" '{print $2}'`
echo 具体行号:$LINE
if [[ -z $LINE  ]]
thenecho 未匹配exit
fi
VERSIONOLDCONTENT=`sed -n ''"$((LINE+1))"'p' ${FILENAME}| grep '[0-9a-zA-Z\.-]+' -Eo | sed -n '2p'`
echo ${VERSIONOLDCONTENT}
#gsed -i  ''"$((LINE+1))"'c\'"${NEWCONTENT}"'' pom.xml
sed -i "" ''"$((LINE+1))"'s/'"${VERSIONOLDCONTENT}"'/'"${NEWCONTENT}"'/' ${FILENAME}
  • 其实逻辑很简单,主要就是寻找 groupIdartifactId ,最后确定好 version 对应的行号将最新的日期后缀版本进行填充进去。
  • 填充呢肯定需要三剑客中的 SED 进行操作,那就需要先获取到以前的旧版本,然后进行替换操作。

为什么使用SHELL

  • shell脚本作为后端程序猿必备技能选择他进行实现也是为了温故下shell的知识。基本上脚本离不开三剑客,换句话说会了三剑客你就可以做好半个运维工作了。
  • 有了这个脚本我每次在功能开发完成之后,会通过SHELL脚本进行版本升级以及自己分支合并到dev分支,这样方便别人获取到最新的代码。

python实现

  • SHELL脚本定制度很高,很难做到自动的兼容功能。比如上面我们在定位包的时候是通过grep进行定位的,正常情况下应该是没什么问题的,但是当pom.xml 出现被注释的同名坐标时或者说名称存在其他相似度的情况下很难保证SHELl脚本还能够正确的解析出来。
  • 除此之外还有一个重要的原因就是SHELL脚本很难在windows 运行,为了能够兼顾到windows电脑我决定用python 进行重新实现该功能。

文件思考

  • 与SHELL不同的是 python处理需要考虑文件格式的问题。SHELL中不管什么格式都是通过三剑客进行定位处理,这是他的优点也是他的缺点。
  • 首先我们得知道 pom.xml 文件他是一个 XML 格式的文件, XML=eXtensible Markup Language 。即是一种可扩展的标记语言。它与 JSON 一样主要用来存储和传输数据。在之前的Springboot章节中我们也实现了如何实现接口传递 XML 数据结构。

常见的 XML 编程接口有 DOM 和 SAX,这两种接口处理 XML 文件的方式不同,当然使用场合也不同。

Python 有三种方法解析 XML,SAX,DOM,以及 ElementTree:

### 1.SAX (simple API for XML )Python 标准库包含 SAX 解析器,SAX 用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。### 2.DOM(Document Object Model)将 XML 数据在内存中解析成一个树,通过对树的操作来操作XML。### 3.ElementTree(元素树)
  • 而我所采用的就是最后一种方式 ElementTree

xml.etree.ElementTree

官网直通车

  • ElementTree 在 python3中已经作为标准库存在了,所以这里不需要我们额外的安装。
基于事件和基于文档的APID来解析XML,可以使用XPath表达式搜索已解析的文件,具有对文档的增删改查的功能,该方式需要注意大xml文件,因为是一次性加载到内存,所以如果是大xml文件,不推荐使用该模块解析,应该使用sax方式
  • 不能说最好只能说他是合适的工具,因为 pom.xml 文件不会很大的。ElementTree 通过 XPath 进行节点选择,所以关于xml 节点查找我们可以参考 xpath 语法即可。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.github.zxhTom</groupId><artifactId>bottom</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>bottom</name><url>http://maven.apache.org</url><description>最底层的繁琐封装</description><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><log4j2.version>2.10.0</log4j2.version></properties><dependencies><!-- 20180927提供了针对stirng bean list 等判断的操作。不用我们在详细的判断了 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.7</version></dependency><!-- 提供了针对list 等判断的操作。不用我们在详细的判断了 --><dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version></dependency><!-- jsonobeject jar包依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.28</version></dependency><!-- 日志记录 --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>${log4j2.version}</version></dependency><!-- 通过反射获取标有注解的类 --><dependency><groupId>org.reflections</groupId><artifactId>reflections</artifactId><version>0.9.10</version></dependency></dependencies>
</project>
  • 上面的 pom.xml 摘自于 com.github.zxhTom 的 bottom 项目中。里面的恰好出现了注释,方便我们后期测试。

解析xml

import xml.etree.ElementTree as ET
with open('pom.xml', 'tr', encoding='utf-8') as rf:tree = ET.parse(rf)print(tree)

  • 实际有效的内容就是 ET.parse 即可解析出来 xml 。

查看pom.xml所有节点标签名称

import xml.etree.ElementTree as ET
with open('pom.xml', 'tr', encoding='utf-8') as rf:tree = ET.parse(rf)
# 根据tree进行遍历
for node in tree.iter():print(node.tag)

读取xml中dependency

  • 我想看下所有的 dependency 标签,只有这样我才能够匹配是否是我需要的那个maven坐标。
import xml.etree.ElementTree as ET
with open('pom.xml', 'tr', encoding='utf-8') as rf:tree = ET.parse(rf)
# tree遍历
for node in tree.findall('.//dependency'):print(node.tag)
  • 上述代码很明显是错误的,因为我们执行脚本后没有任何的输出,至于为什么是这样呢?你可以翻到上一节就可以看到我们在打印所有节点标签名称的时候前面好像都多了一串地址。
  • 这个地址就是 pom.xml 中的命名空间,在跟节点 project 标签中设置的xmlns 属性。至于为什么需要这个呢?每个xml 标签内容都是自定义的,比如你可以将dependency用来做版本号的作用,只要你自己解析的时候注意就行了。而maven中将dependency作为引入坐标的概念,每个人的想法不一,所以引入命名空间,在指定的命名空间中标签的作用是唯一的,这就是 xmlns 存在的意义。
import xml.etree.ElementTree as ET
with open('pom.xml', 'tr', encoding='utf-8') as rf:tree = ET.parse(rf)
# tree遍历
for node in tree.findall('.//{http://maven.apache.org/POM/4.0.0}dependency'):print(node.tag)

读取com.alibaba.fastjson 的版本号

  • 通过上面的内容我们知道在定位节点时候需要加入命名空间。

  • 首先我们知道 com.alibaba.fastjson 的版本号是 1.2.28

import xml.etree.ElementTree as ET
with open('pom.xml', 'tr', encoding='utf-8') as rf:tree = ET.parse(rf)
# tree遍历
for node in tree.findall('.//{http://maven.apache.org/POM/4.0.0}dependency'):groupIdNode=node.find('.{http://maven.apache.org/POM/4.0.0}groupId')artifactNode=node.find('.{http://maven.apache.org/POM/4.0.0}artifactId')if(artifactNode.text=='fastjson' and groupIdNode.text=='com.alibaba'):print(node.find('.{http://maven.apache.org/POM/4.0.0}version').text)
  • 通过 python3 upgrade.py 即可打印出 1.2.28

保存xml

  • 说了这么多,还记得我们一开始的任务吗,没错就是修改掉pom.xml 中指定jar的版本号。这里就将 com.alibaba.fastjson的版本号升级为1.2.29 吧。
import xml.etree.ElementTree as ET
with open('pom.xml', 'tr', encoding='utf-8') as rf:tree = ET.parse(rf)
# tree遍历
for node in tree.findall('.//{http://maven.apache.org/POM/4.0.0}dependency'):groupIdNode=node.find('.{http://maven.apache.org/POM/4.0.0}groupId')artifactNode=node.find('.{http://maven.apache.org/POM/4.0.0}artifactId')if(artifactNode.text=='fastjson' and groupIdNode.text=='com.alibaba'):node.find('.{http://maven.apache.org/POM/4.0.0}version').text='1.2.29'
tree.write('pom.xml')
  • 修改还是很容易的,我们只需要将node.text进行重新赋值就行了。但是经过对比我发现保存后的 pom.xml 好像并不是只改了version标签的内容。

    • 标签多了ns0前缀。
    • 中文乱码
    • 注释丢失

API追踪

def write(self, file_or_filename,encoding=None,xml_declaration=None,default_namespace=None,method=None, *,short_empty_elements=True):
  • 上述提到了三个问题都是很常见的问题,因为我们打开 /Lib/xml/etree/ElementTree.py 源码就能够看到在写会 xml 文件的时候我们一共有7个参数可选,其中第一个self 没啥好说的。
属性 作用
file_or_filename 文件
encoding 输出的编码格式;默认US-ASCII
xml_declaration 将XML声明添加到文件中: True添加;False不添加;None在非US-ASCII 或 UTF-8 或 Unicode时添加; 默认None
default_namespace 默认的命名空间
method xml、 html 、 text。默认 xml
short_empty_elements 空内容的标签时的处理

修改xml后节点多了前缀ns0

  • 很明显是我们没有指定输出的默认命名空间导致程序自动生成一个前缀。
tree.write('pom.xml',default_namespace='http://maven.apache.org/POM/4.0.0')
  • 当我们指定了命名空间,这个时候再查看下文件节点的前缀问题就解决了。

中文乱码

  • 中文乱码就是我们没有指定编码格式。大家都知道默认的 US-ASCII 都是人家老外的东西,我们国内想正常使用肯定还是需要 UTF-8 的。
tree.write('pom.xml',default_namespace='http://maven.apache.org/POM/4.0.0',encoding='UTF-8')
  • 这里我也就不截图了,笔者亲测是可以解决中文乱码的问题的。

标准化xml

  • 这不算个问题,不知道你有没有发现上面我提供的 pom.xml 严格意义上来说不是一个标准的 xml 文件,就好比你上学不带红领巾就不是一个标准学生一样,上面的pom.xml 确实了xml的标准开头申明。不过没关系我们在wirte的时候注意到有一个参数 xml_declaration 就是控制是否生成标准申明的。

修改xml后原来的注释丢了

  • 关于注释丢了这种问题,怎么说呢?无关紧要吧但是往往注释是进行解释说明的,为了追求完美我还是希望能够将注释保留下来。关于注释的问题我们还得简单查看下源码说明。
with open('pom.xml', 'tr', encoding='utf-8') as rf:tree = ET.parse(rf)
  • parse 对应的就是 ElementTree 的源码中 parse 中。

  • 到了这里我们应该不难看出,在渲染的时候主要是通过 TreeBuilder 进行渲染的,其中 CommentHandler 就是 TreeBuilder.comment 进行的。那么我们继续查看下 TreeBuilder 的源码来查看为什么默认的 parser 没有保留下注释。

  • 最终就是 _handle_single 方法进行创建节点的,comment中创建节点的工厂类是_comment_factory , 而这个类就是产生 Comment 。 因为insert_comments=False , 所以默认的TreeBuilder对于注释只会产生一个空的Comment对象,所以我们通过ET.write出来的pom.xml中关于注释部分就是空白代替,就是因为这个原因。
  • 到了这里想要保留注释也就变得非常简单了,那么我们只需要修改TreeBuilder就可以了。
class CommentedTreeBuilder (ET.TreeBuilder):def comment(self,data):self.start(ET.Comment,{})self.data(data)self.end(ET.Comment)
  • 上述代码类似于 Java中继承了 TreeBuilder 并且重写了 comment方法。主要的逻辑就是开头一个注释标签属性为空,结尾是一个注释标签结尾,中间的内容是我们注释的内容,所以综上就是保留下来注释。

  • 重写了类之后我们还需要在ET中指定我们自定义的TreeBuilder 。

parser = ET.XMLParser(target=CommentedTreeBuilder())
tree = ET.parse(xml_path,parser=parser)
  • 构建好tree之后其他就是一样的操作,最终倒出来的文件就是我们保留注释的xml 了。

获取不到子节点

  • 就的版本是 getchildren , 但是在新版本中直接废弃了这个方法。而在新版本中是通过 iter(tag) 进行创建迭代器的。但tag=None或者 * 表示所有子孙节点,其他的情况就只查找指定tag 名称的集合。

优雅解析带命名空间的xml

  • 还记得我们是如何解析获取到 pom.xml 中的标签的。
# tree遍历
for node in tree.findall('.//{http://maven.apache.org/POM/4.0.0}dependency'):print(node.tag)
  • 这种方式我只想说NO , 每次在通过xpath 定位节点都需要添加前缀,这简直是个噩梦。
ns = {'real_mvn': 'http://maven.apache.org/POM/4.0.0','vistual': 'http://characters.zxhtom.com'}
# tree遍历
for node in tree.findall('.//real_mvn:dependency',ns):print(node.tag)

总结

  • 本文主要是通过脚本实现pom.xml 的版本升级功能。SHELL通过三剑客定位到指定坐标进行版本更新,缺点是可能不兼容注释同内容的坐标,优点是修改内容范围小,不会造成大影响
  • 而python实现的xml文件修改。他的优点是能够精准定位不会产生错误定位问题且支持注释内容保留,缺点是不适合操作大文件。
文件 dependency数量
knife4j-spring-ui-1.9.6.pom 0
spring-context-support-1.0.6.pom 3
spring-core-5.1.7.RELEASE.pom 10
springfox-swagger-common-3.0.0.pom 11

其他说明

参考文章

python3去除注释

elementtree中文文档

python自动更新pom文件相关推荐

  1. python+pyinstaller+OOS自动更新(2) 文件遍历创建filehash.txt

    python+pyinstaller+OOS自动更新2 文件遍历创建filehash.txt 遍历文件 获取文件的md5 /tool_getFilehash.py的所有代码 这时就可以打包tool_g ...

  2. python自动读取excel文件邮箱列表,自动批量发送邮件项目(附使用方法+代码)

    python自动发送邮件项目 项目简介 使用方法 项目结构 完整项目代码 结语 项目简介 该项目功能是利用python自动读取excel文件中的邮箱列表,并批量发送邮件到目标邮箱,进行客户挖掘的.该项 ...

  3. python自动更新excel_Python办公自动化(六)|自动更新表格,告别繁琐

    今天我们讲解的案例是如何使用Python自动更新Excel表格,简单来说就是每天都会对Excel中多个sheet进行更新,需要操作完后可以用程序完成第一张sheet 汇总表的更新,大概就是这样 当然实 ...

  4. idea自动更新java_IntelliJ IDEA自动更新资源文件

    在 Java Web 开发中, 一般更新了文件(例如jsp.css.js)后要手动重启 Tomcat 服务器, 才能生效, 生命宝贵啊.其实我简单得配置一下IntelliJ IDEA,即可实现自动更新 ...

  5. Wix 安装部署教程(十二) -- 自动更新WXS文件

    原文:Wix 安装部署教程(十二) -- 自动更新WXS文件 上一篇分享了一个QuickWIX,用来对比两个工程前后的差异,但是这样还是很繁琐,而且昨天发现有Bug,目录对比有问题.这次改变做法,完全 ...

  6. python制作相册_《自拍教程73》Python 自动生成相册文件夹

    这里将告诉您<自拍教程73>Python 自动生成相册文件夹,具体操作过程:案例故事: 接Python mediainfo批量重命名图片文件,测试图片是批量重命名好了, 但是将测试图片放于 ...

  7. iOS13屏蔽自动更新描述文件

    利用tvOS描述文件屏蔽iOS13更新,使用Safari浏览器打开,然后允许在设置里面安装描述文件并重启设备,重启后就会屏蔽OTA更新了,有需要的可以试试. iOS13屏蔽自动更新描述文件: 复制地址 ...

  8. python自动更新excel_Python办公自动化|自动更新表格,告别繁琐

    大家好,又到了Python办公自动化专题.今天我们讲解的案例是如何使用Python自动更新Excel表格,简单来说就是每天都会对Excel中多个sheet进行更新,需要操作完后可以用程序完成第一张sh ...

  9. python自动更新excel_Python杀死了Excel|自动更新表格,告别繁琐

    大家好,又到了Python办公自动化专题. 今天我们讲解的案例是如何使用Python自动更新Excel表格,简单来说就是每天都会对Excel中多个sheet进行更新,需要操作完后可以用程序完成第一张s ...

最新文章

  1. 标准K-means算法的缺陷、K-mean++初始化算法、初始化算法步骤、Kmeans++算法实现
  2. 语义分割--Learning Object Interactions and Descriptions for Semantic Image Segmentation
  3. idea mysql错误提示_idea提示错误:java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
  4. pytorch —— 模型容器与AlexNet构建
  5. oracle修改字符集_oracle修改数据库字符集
  6. 快手通过港交所聆讯 2020年前三季度经调亏损72亿元
  7. git常用命令与常见问题解决办法
  8. MySQL中Cardinality值的介绍
  9. 学习C语言中的位操作
  10. Db4o数据库:快速入门
  11. [JPA错误]javax.persistence.EntityNotFoundException: Unable to find xxx
  12. python重启路由器_利用python重启路由器
  13. 数组输出c语言getchar,C语言入门 — getchar
  14. 使用Python绘制精美绝伦的股票行情K线图
  15. 设计出python_《设》字意思读音、组词解释及笔画数 - 新华字典 - 911查询
  16. Go 语言之 go-app
  17. 云计算期末习题含答案
  18. FBX SDK 总结之中文问题
  19. 【硬件设计】AD 生成 Gerber 文件
  20. 精读《什么是 LOD 表达式》

热门文章

  1. Hbuilder插件下载失败,提示“插件下载失败,请检查网络是否正常”
  2. Python 爬取 5K 分辨率超清唯美壁纸
  3. 亿级流量网站架构核心技术 跟开涛学搭建高可用高并发系统.
  4. VVC/H.266代码阅读(VTM8.0)(三. Slice到CTU的处理 )
  5. Python 学习归纳
  6. Django计算机毕业设计JAVA高校田径运动会管理python(源码程序+lw+远程部署)
  7. react 日历组件拖拽部分逻辑/元素
  8. 黑马JAVA P54 方法常见问题
  9. 最详细的VGG模型理解
  10. 3.7 来电显示号码归属地