淺談前端組件發(fā)布自動(dòng)化
從09年angularJS被谷歌推出以來(lái),涌現(xiàn)了很多前端應(yīng)用的開(kāi)發(fā)、設(shè)計(jì)等方面優(yōu)秀的解決方案(框架 、庫(kù)、設(shè)計(jì)規(guī)范)。隨著SPA的普及,"Web Components"的設(shè)計(jì)標(biāo)準(zhǔn)漸漸開(kāi)始影響各個(gè)框架的設(shè)計(jì)方向,開(kāi)發(fā)人員的邏輯封裝也不再像以前效率相對(duì)低下,優(yōu)秀的框架給開(kāi)發(fā)者提供了簡(jiǎn)潔且快速的組件化解決方案。
本文會(huì)盡量少的貼代碼,盡量少的談及工程化本身的東西,旨在分享組件自動(dòng)化基礎(chǔ)設(shè)施建設(shè)過(guò)程中的些許經(jīng)驗(yàn)。
工程化的內(nèi)容后續(xù)會(huì)有單獨(dú)的“毒文”來(lái)禍害大家。
一、應(yīng)用背景
如上所述,中大型的團(tuán)隊(duì)或公司在未來(lái)的一段時(shí)間,都需要封裝足夠多且完善的UI組件來(lái)支撐快速開(kāi)發(fā)和降低學(xué)習(xí)成本。這樣的情境下,就會(huì)有一些人,去進(jìn)行組件的標(biāo)準(zhǔn)化開(kāi)發(fā),快速迭代,而隨之而來(lái)且亟待解決的問(wèn)題:
- 組件的版本控制
- 組件的產(chǎn)出物(包、文檔)的相關(guān)性
- 組件構(gòu)建、測(cè)試的冗長(zhǎng)的工作量
- 組件發(fā)布環(huán)節(jié)的不可控
- 權(quán)限的不可控性(組件倉(cāng)庫(kù)權(quán)限、源碼倉(cāng)庫(kù)權(quán)限)
組件發(fā)布自動(dòng)化的目的不僅僅是減少你的手動(dòng)構(gòu)建、發(fā)布的工作量,它的本質(zhì)是對(duì)多人協(xié)同組件開(kāi)發(fā)方式的規(guī)范和管控。
沒(méi)有這些你的前端研發(fā)工作也能支撐?能用和好用是兩個(gè)概念。
二、涉眾及相關(guān)技術(shù)棧
1、人員涉眾
- 開(kāi)發(fā)人員:組件開(kāi)發(fā)者,源碼的提交者
- 配置管理員:版本發(fā)布的流程、權(quán)限控制者,同時(shí)也是發(fā)布節(jié)奏的控制者
2、相關(guān)技術(shù)名詞
了解以下,閱讀過(guò)程會(huì)相對(duì)輕松:
- nodeJS:自動(dòng)化流程中的主要參與者
- gradle:負(fù)責(zé)整體流程的包裝,同時(shí)也會(huì)用到其生態(tài)中的一些插件
- jenkinsFile:推薦用聲明式(groovy),本文基于腳本式
- webdav:用來(lái)做文檔發(fā)布的服務(wù)器(優(yōu)勢(shì)是可控的發(fā)布權(quán)限),可以用其他web容器替代
3、工程職責(zé)
為了詳略得當(dāng),這里列舉工程的一些基礎(chǔ)功能(這里默認(rèn)已經(jīng)實(shí)現(xiàn),實(shí)際需要我們自己根據(jù)我們的實(shí)際情況去實(shí)現(xiàn)),以免贅述與本文不相關(guān)的內(nèi)容。
- 包輸出:工程提供構(gòu)建的基礎(chǔ)功能(將源碼輸出為npm倉(cāng)庫(kù)的package的功能)
- 文檔輸出:工程提供文檔輸出的功能(推薦markdown手寫合成的體驗(yàn)較好的文檔輸出機(jī)制)
- 測(cè)試:提供單元或e2e測(cè)試的基礎(chǔ)框架
三、流程概覽

如上圖所示,發(fā)布流程主要為(區(qū)別化的內(nèi)容加粗):
- 開(kāi)發(fā)人員提交代碼(git push)/配置管理員打tag
- git接到push/tag,根據(jù)配置好的webhook給jenkins發(fā)請(qǐng)求
- jenkins接收請(qǐng)求并觸發(fā)指定的task,根據(jù)jenkinsFile執(zhí)行pipeline:
- 更新項(xiàng)目并檢查設(shè)置
- sonar:推送代碼到sonar
- 測(cè)試:?jiǎn)卧獪y(cè)試&&e2e測(cè)試
- package構(gòu)建
- package發(fā)布:計(jì)算版本號(hào)并發(fā)布package
- 根據(jù)git 的最后一條log計(jì)算下一版本號(hào)
- 根據(jù)git的最后一條log區(qū)分發(fā)布測(cè)試版本(snapshots)還是正式版本(release)
- 構(gòu)建doc
- 發(fā)布doc:根據(jù)git的最后一條log區(qū)分發(fā)布測(cè)試版本文檔(snapshots)還是正式版本文檔(release)
四、流程詳述(冗長(zhǎng)的細(xì)節(jié),可略)
1、update project - 更新項(xiàng)目并檢查設(shè)置
echo 'update source'
checkout scm
echo 'reset config'
sh './gradlew widget-pkg-reset-config -q'
echo 'update dependencies'
sh 'npm install && npm update'
-
更新(初始化)源碼:jenkins行為,更新項(xiàng)目源碼
checkout SCM(也可用gradle或node執(zhí)行代碼更新) - 更新(初始化)gradle依賴:gradle行為,根據(jù)gradle配置,更新gradle相關(guān)依賴
- 更新(初始化)npm依賴:node行為,根據(jù)pkg版本,更新相關(guān)依賴(可能為運(yùn)行時(shí)依賴,也可能為開(kāi)發(fā)時(shí)依賴)
- 檢查配置: gradle包裝(node行為),檢查當(dāng)前構(gòu)建環(huán)境的正確性,如當(dāng)前所需的環(huán)境變量、當(dāng)前所設(shè)置的倉(cāng)儲(chǔ)地址、當(dāng)前所需要的所有構(gòu)建環(huán)境的版本等,如缺失則發(fā)出警告并中斷集成,如不正確則重置正確環(huán)境設(shè)置。
2、test - 測(cè)試
暫略。
3、build source - 構(gòu)建資源
echo 'Building source..'
sh 'npm run pre-release'
echo 'Build source completed'
- 構(gòu)建資源:node行為,進(jìn)行資源構(gòu)建。
4、publish source
sh './gradlew widget-pkg-modify-version -q'
sh './gradlew widget-pkg-set-config -q'
echo 'Publish source..'
sh 'npm run publish-to-npm'
-
修改版本號(hào): gradle包裝(node行為),根據(jù)插件計(jì)算出的
currentVersion,臨時(shí)修改當(dāng)前構(gòu)建結(jié)果中的package.json文件的version字段。 -
設(shè)置相關(guān)發(fā)布配置:gradle包裝(node行為)。
- 根據(jù)當(dāng)前版本狀態(tài)選擇發(fā)布的倉(cāng)庫(kù)
- 根據(jù)當(dāng)前環(huán)境變量設(shè)置當(dāng)前發(fā)布倉(cāng)儲(chǔ)的認(rèn)證信息
- 發(fā)布資源:node行為,發(fā)布構(gòu)建資源。
5、build doc
echo 'Building doc..'
sh 'npm run build'
echo 'Build doc completed'
- 構(gòu)建資源:node行為:構(gòu)建當(dāng)前使用手冊(cè)
6、publish doc
echo 'Publish doc..'
sh './gradlew widget-docs-publish -q'
echo 'source doc completed'
- 清除已發(fā)布文擋:gradle行為,嘗試清除已經(jīng)發(fā)布在webdav上的當(dāng)前版本文檔(為了防止文擋重復(fù)發(fā)布的文件冗余,因?yàn)榍岸遂o態(tài)有文件名有MD5后綴,重復(fù)發(fā)布無(wú)法覆蓋)。
-
修改文擋資源的BASE:gradle包裝(node行為),根據(jù)文擋發(fā)布需要,在webdav的服務(wù)目錄下,文件夾深度決定url上下文層級(jí),構(gòu)建后的靜態(tài)為angular工程,需要上下文,否則無(wú)法正常調(diào)用web history,所以需要根據(jù)當(dāng)前的
currentVersion修改當(dāng)前文檔資源的base href。 - 發(fā)布文檔:gradle行為,調(diào)用gradle插件進(jìn)行遞歸發(fā)布。
五、專項(xiàng)剖析
1、不同層級(jí)的行為及調(diào)用關(guān)系分析

2、版本的計(jì)算
2.1、版本發(fā)布到正式、測(cè)試倉(cāng)庫(kù)的判斷規(guī)則
檢查當(dāng)前倉(cāng)儲(chǔ)的最后一次行為(git log):
- 如果沒(méi)有tag,版本從0.1.0-SNAPSHOT開(kāi)始
- 如果有tag,但最新的commit不是tag,則根據(jù)語(yǔ)義規(guī)范發(fā)布相應(yīng)版本的SNAPSHOT版本
- 如果有tag,且最新的commit是tag,則根據(jù)tag發(fā)布
2.2、版本遞增計(jì)算規(guī)則
請(qǐng)綜合參照:
3、如何指定版本號(hào)發(fā)布
在發(fā)布時(shí)遇到的一個(gè)問(wèn)題,java的包發(fā)布的版本可以通過(guò)gradle變量來(lái)指定,那前端的package的版本號(hào)如何動(dòng)態(tài)指定?在npm文檔搜索無(wú)果后,決定寫插件去修改“package.json”的version來(lái)指定發(fā)布版本。
為了避免導(dǎo)致其他問(wèn)題,建議在初始化的步驟將“package.json”進(jìn)行checkout。
五、總結(jié)和思考
1、如何在“簡(jiǎn)化技術(shù)棧”與“前后端自動(dòng)化統(tǒng)一”上做出取舍?
這其中的一個(gè)關(guān)鍵點(diǎn)為,前端自動(dòng)化為什么要使用gradle。
在本文的實(shí)踐中,受我們團(tuán)隊(duì)已經(jīng)存在后端的自動(dòng)化工作流的影響,為了與其統(tǒng)一版本的升級(jí)規(guī)則,會(huì)用到gradle的“axion-release-plugin”插件,以及在發(fā)布到webdav時(shí)會(huì)用到“uk.co.firstzero.webdav”,出于前后端統(tǒng)一和不重復(fù)造輪子考慮,前端才增加gradle的使用。
那么我們不用gradle可以嗎?當(dāng)然可以,你只需要以上提到的內(nèi)容用node再造一遍輪子,祝你好運(yùn)。
送一個(gè)版本計(jì)算的node輪子(不再更新,但可供參考):axion-release-node-plugin
2、前端有各種lint,sonar相對(duì)前端的意義大嗎?
后續(xù)補(bǔ)充。