python自動更新pom文件的方法
前言
項目越來越多,版本管理越來越麻煩,在項目上我使用 maven version
來進(jìn)行版本管理。主要還是在分布式項目中模塊眾多的場景中使用,畢竟各個模塊對外的版本需要保持統(tǒng)一。
關(guān)于這個插件如何使用呢?也是非常的簡單。只需要在maven視圖中進(jìn)行設(shè)置版本號即可將分模塊項目的版本進(jìn)行升級了。
除了idea插件外,maven本身也提供了一個版本管理工具 versions-maven-plugin
。 具體用法以后有機會在贅述。
自定義實現(xiàn)版本更新
- 作為一個專業(yè)懶人,我還是覺得idea的插件不夠智能,確切的說還不夠自動化。之前我已經(jīng)動手實現(xiàn)了防 jenkins 自動打包上傳啟動服務(wù)的腳本的功能了,難道提交合并代碼這種簡單的事情還需要我自己處理嗎。不得不承認(rèn)代碼沖突了的確還是需要認(rèn)為干涉的,但是在平時開發(fā)中有多少概率會發(fā)生代碼沖突呢?我們都是分工合作基本上代碼沖突概率很低。
- 關(guān)于代碼提交,自己分支如何合并到dev, 如何保證自己分支代碼最新等等這些場景我們只需要通過腳本來進(jìn)行自動化操作就行了。針對這些功能場景我大概寫了兩個腳本
batfhmerge.sh
和batchgrade.sh
就搞定了。 - 然而我想說的是關(guān)于項目的版本如何升級。上面也提到了我們分工合作就必然涉及到別人使用你的jar的場景了。你可以使用
SNAPSHOT
版本來保證別人拉取到你最新的功能代碼,但是有些公司會要求使用非SNAPSHOT
版本進(jìn)行管理也就是正式版本,這樣做的好處就是容易找到之前的版本代碼功能。
SHELL 實現(xiàn)
之前用SHELL 實現(xiàn)了自動更新置頂項目的版本號為最新日期后綴。雖然使用起來沒發(fā)現(xiàn)有什么BUG, 但是感覺代碼實現(xiàn)上還是很弱智的。
# 該腳本主要用來升級發(fā)包期間修改各服務(wù)版本 FILEPATH=$1 GROUPID=$2 ARTIFACTID=$3 FILENAME=$4 while getopts ":f:g:a:" opt do case $opt in f) FILENAME=$OPTARG echo "您輸入的文件配置:$FILENAME" ;; g) GROUPID=$OPTARG echo "您輸入的groupid配置:$GROUPID" ;; a) ARTIFACTID=$OPTARG echo "您輸入的artifactid配置:$ARTIFACTID" ;; ff) FILENAME=$OPTARG echo "您輸入的帶修改文件為:$FILENAME" ;; ?) echo "未知參數(shù)" 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 ]] then echo 未匹配 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}
- 其實邏輯很簡單,主要就是尋找
groupId
和artifactId
,最后確定好version
對應(yīng)的行號將最新的日期后綴版本進(jìn)行填充進(jìn)去。 - 填充呢肯定需要三劍客中的
SED
進(jìn)行操作,那就需要先獲取到以前的舊版本,然后進(jìn)行替換操作。
為什么使用SHELL
- shell腳本作為后端程序猿必備技能選擇他進(jìn)行實現(xiàn)也是為了溫故下shell的知識?;旧夏_本離不開三劍客,換句話說會了三劍客你就可以做好半個運維工作了。
- 有了這個腳本我每次在功能開發(fā)完成之后,會通過SHELL腳本進(jìn)行版本升級以及自己分支合并到dev分支,這樣方便別人獲取到最新的代碼。
python實現(xiàn)
- SHELL腳本定制度很高,很難做到自動的兼容功能。比如上面我們在定位包的時候是通過grep進(jìn)行定位的,正常情況下應(yīng)該是沒什么問題的,但是當(dāng)pom.xml 出現(xiàn)被注釋的同名坐標(biāo)時或者說名稱存在其他相似度的情況下很難保證SHELl腳本還能夠正確的解析出來。
- 除此之外還有一個重要的原因就是SHELL腳本很難在windows 運行,為了能夠兼顧到windows電腦我決定用python 進(jìn)行重新實現(xiàn)該功能。
文件思考
- 與SHELL不同的是 python處理需要考慮文件格式的問題。SHELL中不管什么格式都是通過三劍客進(jìn)行定位處理,這是他的優(yōu)點也是他的缺點。
- 首先我們得知道
pom.xml
文件他是一個 XML 格式的文件, XML=eXtensible Markup Language 。即是一種可擴(kuò)展的標(biāo)記語言。它與 JSON 一樣主要用來存儲和傳輸數(shù)據(jù)。在之前的Springboot章節(jié)中我們也實現(xiàn)了如何實現(xiàn)接口傳遞 XML 數(shù)據(jù)結(jié)構(gòu)。
常見的 XML 編程接口有 DOM 和 SAX,這兩種接口處理 XML 文件的方式不同,當(dāng)然使用場合也不同。
Python 有三種方法解析 XML,SAX,DOM,以及 ElementTree:
### 1.SAX (simple API for XML ) Python 標(biāo)準(zhǔn)庫包含 SAX 解析器,SAX 用事件驅(qū)動模型,通過在解析XML的過程中觸發(fā)一個個的事件并調(diào)用用戶定義的回調(diào)函數(shù)來處理XML文件。 ### 2.DOM(Document Object Model) 將 XML 數(shù)據(jù)在內(nèi)存中解析成一個樹,通過對樹的操作來操作XML。 ### 3.ElementTree(元素樹)
而我所采用的就是最后一種方式 ElementTree
。
xml.etree.ElementTree
ElementTree
在 python3中已經(jīng)作為標(biāo)準(zhǔn)庫存在了,所以這里不需要我們額外的安裝。
基于事件和基于文檔的APID來解析XML,可以使用XPath表達(dá)式搜索已解析的文件,具有對文檔的增刪改查的功能,該方式需要注意大xml文件,因為是一次性加載到內(nèi)存,所以如果是大xml文件,不推薦使用該模塊解析,應(yīng)該使用sax方式
不能說最好只能說他是合適的工具,因為 pom.xml
文件不會很大的。ElementTree
通過 XPath
進(jìn)行節(jié)點選擇,所以關(guān)于xml 節(jié)點查找我們可以參考 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 等判斷的操作。不用我們在詳細(xì)的判斷了 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> <!-- 提供了針對list 等判斷的操作。不用我們在詳細(xì)的判斷了 --> <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> <!-- 通過反射獲取標(biāo)有注解的類 --> <dependency> <groupId>org.reflections</groupId> <artifactId>reflections</artifactId> <version>0.9.10</version> </dependency> </dependencies> </project>
上面的 pom.xml
摘自于 com.github.zxhTom
的 bottom 項目中。里面的恰好出現(xiàn)了注釋,方便我們后期測試。
解析xml
import xml.etree.ElementTree as ET with open('pom.xml', 'tr', encoding='utf-8') as rf: tree = ET.parse(rf) print(tree)
- 實際有效的內(nèi)容就是
ET.parse
即可解析出來 xml 。
查看pom.xml所有節(jié)點標(biāo)簽名稱
import xml.etree.ElementTree as ET with open('pom.xml', 'tr', encoding='utf-8') as rf: tree = ET.parse(rf) # 根據(jù)tree進(jìn)行遍歷 for node in tree.iter(): print(node.tag)
讀取xml中dependency我想看下所有的 dependency 標(biāo)簽,只有這樣我才能夠匹配是否是我需要的那個maven坐標(biāo)。
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)
- 上述代碼很明顯是錯誤的,因為我們執(zhí)行腳本后沒有任何的輸出,至于為什么是這樣呢?你可以翻到上一節(jié)就可以看到我們在打印所有節(jié)點標(biāo)簽名稱的時候前面好像都多了一串地址。
- 這個地址就是
pom.xml
中的命名空間,在跟節(jié)點 project 標(biāo)簽中設(shè)置的xmlns
屬性。至于為什么需要這個呢?每個xml 標(biāo)簽內(nèi)容都是自定義的,比如你可以將dependency用來做版本號的作用,只要你自己解析的時候注意就行了。而maven中將dependency作為引入坐標(biāo)的概念,每個人的想法不一,所以引入命名空間,在指定的命名空間中標(biāo)簽的作用是唯一的,這就是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 的版本號
- 通過上面的內(nèi)容我們知道在定位節(jié)點時候需要加入命名空間。
- 首先我們知道
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
說了這么多,還記得我們一開始的任務(wù)嗎,沒錯就是修改掉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進(jìn)行重新賦值就行了。但是經(jīng)過對比我發(fā)現(xiàn)保存后的
pom.xml
好像并不是只改了version標(biāo)簽的內(nèi)容。- 標(biāo)簽多了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個參數(shù)可選,其中第一個self 沒啥好說的。
屬性 | 作用 |
---|---|
file_or_filename | 文件 |
encoding | 輸出的編碼格式;默認(rèn)US-ASCII |
xml_declaration | 將XML聲明添加到文件中: True添加;False不添加;None在非US-ASCII 或 UTF-8 或 Unicode時添加; 默認(rèn)None |
default_namespace | 默認(rèn)的命名空間 |
method | xml、 html 、 text。默認(rèn) xml |
short_empty_elements | 空內(nèi)容的標(biāo)簽時的處理 |
修改xml后節(jié)點多了
前綴ns0很明顯是我們沒有指定輸出的默認(rèn)命名空間導(dǎo)致程序自動生成一個前綴。
tree.write('pom.xml',default_namespace='http://maven.apache.org/POM/4.0.0')
當(dāng)我們指定了命名空間,這個時候再查看下文件節(jié)點的前綴問題就解決了。
中文亂碼
中文亂碼就是我們沒有指定編碼格式。大家都知道默認(rèn)的 US-ASCII 都是人家老外的東西,我們國內(nèi)想正常使用肯定還是需要 UTF-8
的。
tree.write('pom.xml',default_namespace='http://maven.apache.org/POM/4.0.0',encoding='UTF-8')
這里我也就不截圖了,筆者親測是可以解決中文亂碼的問題的。
標(biāo)準(zhǔn)化xml
這不算個問題,不知道你有沒有發(fā)現(xiàn)上面我提供的 pom.xml 嚴(yán)格意義上來說不是一個標(biāo)準(zhǔn)的 xml 文件,就好比你上學(xué)不帶紅領(lǐng)巾就不是一個標(biāo)準(zhǔn)學(xué)生一樣,上面的pom.xml 確實了xml的標(biāo)準(zhǔn)開頭申明。不過沒關(guān)系我們在wirte的時候注意到有一個參數(shù) xml_declaration
就是控制是否生成標(biāo)準(zhǔn)申明的。
修改xml后原來的注釋丟了
關(guān)于注釋丟了這種問題,怎么說呢?無關(guān)緊要吧但是往往注釋是進(jìn)行解釋說明的,為了追求完美我還是希望能夠?qū)⒆⑨尡A粝聛怼jP(guān)于注釋的問題我們還得簡單查看下源碼說明。
with open('pom.xml', 'tr', encoding='utf-8') as rf: tree = ET.parse(rf)
parse 對應(yīng)的就是 ElementTree 的源碼中 parse 中。
到了這里我們應(yīng)該不難看出,在渲染的時候主要是通過 TreeBuilder
進(jìn)行渲染的,其中 CommentHandler 就是 TreeBuilder.comment 進(jìn)行的。那么我們繼續(xù)查看下 TreeBuilder 的源碼來查看為什么默認(rèn)的 parser 沒有保留下注釋。
- 最終就是 _handle_single 方法進(jìn)行創(chuàng)建節(jié)點的,comment中創(chuàng)建節(jié)點的工廠類是
_comment_factory
, 而這個類就是產(chǎn)生 Comment 。 因為insert_comments=False , 所以默認(rèn)的TreeBuilder對于注釋只會產(chǎn)生一個空的Comment對象,所以我們通過ET.write出來的pom.xml中關(guān)于注釋部分就是空白代替,就是因為這個原因。 - 到了這里想要保留注釋也就變得非常簡單了,那么我們只需要修改TreeBuilder就可以了。
class CommentedTreeBuilder (ET.TreeBuilder): def comment(self,data): self.start(ET.Comment,{}) self.data(data) self.end(ET.Comment)
- 上述代碼類似于 Java中繼承了 TreeBuilder 并且重寫了 comment方法。主要的邏輯就是開頭一個注釋標(biāo)簽屬性為空,結(jié)尾是一個注釋標(biāo)簽結(jié)尾,中間的內(nèi)容是我們注釋的內(nèi)容,所以綜上就是保留下來注釋。
- 重寫了類之后我們還需要在ET中指定我們自定義的TreeBuilder 。
parser = ET.XMLParser(target=CommentedTreeBuilder()) tree = ET.parse(xml_path,parser=parser)
- 構(gòu)建好tree之后其他就是一樣的操作,最終倒出來的文件就是我們保留注釋的xml 了。
獲取不到子節(jié)點
就的版本是 getchildren , 但是在新版本中直接廢棄了這個方法。而在新版本中是通過 iter(tag) 進(jìn)行創(chuàng)建迭代器的。但tag=None或者 * 表示所有子孫節(jié)點,其他的情況就只查找指定tag 名稱的集合。
優(yōu)雅解析帶命名空間的xml
還記得我們是如何解析獲取到 pom.xml 中的標(biāo)簽的。
# tree遍歷 for node in tree.findall('.//{http://maven.apache.org/POM/4.0.0}dependency'): print(node.tag)
這種方式我只想說NO , 每次在通過xpath 定位節(jié)點都需要添加前綴,這簡直是個噩夢。
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)
總結(jié)
- 本文主要是通過腳本實現(xiàn)pom.xml 的版本升級功能。SHELL通過三劍客定位到指定坐標(biāo)進(jìn)行版本更新,缺點是可能不兼容注釋同內(nèi)容的坐標(biāo),優(yōu)點是修改內(nèi)容范圍小,不會造成大影響
- 而python實現(xiàn)的xml文件修改。他的優(yōu)點是能夠精準(zhǔn)定位不會產(chǎn)生錯誤定位問題且支持注釋內(nèi)容保留,缺點是不適合操作大文件。
文件 | dependency數(shù)量 |
---|---|
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 |
其他說明
參考文章
到此這篇關(guān)于python自動更新pom文件的文章就介紹到這了,更多相關(guān)python自動更新內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python實現(xiàn)用手機監(jiān)控遠(yuǎn)程控制電腦的方法
這篇文章主要介紹了Python實現(xiàn)用手機監(jiān)控遠(yuǎn)程控制電腦的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04python pandas dataframe 按列或者按行合并的方法
下面小編就為大家分享一篇python pandas dataframe 按列或者按行合并的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04詳細(xì)解讀Python中解析XML數(shù)據(jù)的方法
這篇文章主要介紹了Python中解析XML數(shù)據(jù)的方法,是Python入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-10-10Python畫圖工具M(jìn)atplotlib庫常用命令簡述
這篇文章主要介紹了Python畫圖Matplotlib庫常用命令簡述總結(jié),文中包含詳細(xì)的圖文示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09python 通過pip freeze、dowload打離線包及自動安裝的過程詳解(適用于保密的離線環(huán)境
這篇文章主要介紹了python 通過pip freeze、dowload打離線包及自動安裝【適用于保密的離線環(huán)境】,本文通圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12Python-flask調(diào)用接口返回中文數(shù)據(jù)問題
這篇文章主要介紹了Python-flask調(diào)用接口返回中文數(shù)據(jù)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03