編號一樣,就必須是一樣。
一、踩坑
由於筆者負責的項目多為公司內部的共用元件,因此常常需要跨單位的協助測試,先前,筆者在開發一個樣態為「Shared-Library」的專案,而協測單位希望筆者在測試環境時就給予「Shared-Library」一個正式版號。
何謂正式版號呢?
以「1.0
」版本來說,通常我們在開發時,都會在其後面加上「SNAPSHOT」的標示,譬如「1.0-SNAPSHOT
」;而在測試環境時,也通常會使用「alpha」或「beta」等特定字元,直到在「Prepunch」時,也就是已通過測試且已定版的物件。
起初,筆者並不清楚「Snapshot」其實是有作用的,而且又想說既然協測單位都這麼要求了,反正於我也沒什麼影響,就盡量配合吧,於是乎,筆者就在測試環境提前替測試用的「Library Jar」標上版號;本來的版號應該是「1.0-SNAPSHOT
」,筆者就它調整為「1.0
」。
當然,完成後就給丟給測試單位進行測試,不過都說了是測試版本,就代表其還不穩定,有可能存在一些問題,果不其然,測出了一些小問題,這也沒什麼好說的,反正就盡快修正,然後再上版就好了。
於是乎,筆者就將問題修正,再通過「CICD」將修正好的新版本推上「Nexus」,接著,當然就是通知協測單位進行覆測。
本以為事情就這麼搞定了,誰知道,協測單位告訴我,測試失敗,幹!
在碰到這個狀況後,筆者第一個猜測是部署失敗,可能是調整後的「Shared-Library」的「Jar」沒有被成功產生或是沒有被推送至「Nexus」伺服器上,因此,筆者立馬去檢查了「Nexus」,但奇怪的是,「Shared-Library」的「Jar」,其最後被更新時間戳記的時間點是如預期的,而且將之下載、解壓縮,然後檢查其中的程式碼,也是修改後的程式碼;所以筆者可以斷定,問在不是出在部署到「Nexus」的過程。
那問題出在哪裡呢?
事實上,問題出在目標專案編譯時,這是因為目標專案在第二次構建時,其所拉取到的「Shared-Library」的「Jar」是已存在本地端的「Jar」;而非是經過調整、已修復問題的「Jar」。
所以是快取機制誤事嗎?
也不是的,順帶一提,我們在編譯專案的指令都會加上參數「-U
」,該參數就是為了避免因為快取機制而包到錯誤的「Jar」所添加的;「-U
」是「--update-snapshots
」的縮寫;意即,當「Maven」在構建專案時,不論本地端是否有存在相同的「Jar」,其都必須到「Nexus」是否檢查有新版,參數說明可以參考「Maven CLI Options Reference」。
竟然不是快取機制導致,那是什麼問題呢?
二、填坑
事實上,其實並沒有出任何問題。
這次的關鍵在於:「Snapshot」。
由於協測單位的要求,筆者將原本應該標註「1.0-SNAPSHOT
」的版本,標註為「1.0
」。
此時,當「Maven」在編譯目標專案時,因為其發現本地端已經存在「Shared-Library」版本為「1.0
」的「Jar」,所以,其就不會再去「Nexus」上更新,即使「Nexus」上「1.0
」的「Jar」與本地端「1.0
」的「Jar」的實際內容是有差異的。
但如果將版本號換成「1.0-SNAPSHOT
」,其行為就不同的,同樣在編譯目標專案,其不會因為本地端已經存在「1.0-SNAPSHOT
」的「Jar」,它還是會去「Nexus」上更新。
我們總結一下上述的內容,「Maven」在編譯專案時,若碰到依賴的「Jar」,於「Release」版本而言,若本地端已經存在指定版號的「Jar」,那麼,即使使用「-U
」參數,它也不會到「Nexus」抓取。
反之,於「Snapshot」版本而言,就算本地端已經存在指定版號的「Jar」,使用「-U
」參數,它還是會到「Nexus」抓取,補充一下,此處的「抓取」並不是真的將整個「Jar」無腦的就強制更新,而是會先去下載其「簽名文件」,其實也就是「Hash」,以確定兩邊資料是否一致。
坑是找到主了,但為什麼會有這樣的差別呢?
這是因為「Maven」認為,「Release」是正式的版號,而每個正式的版本號都代表著一個「狀態」,也僅能代表一個「狀態」,一個不可變的「狀態」,也就是說,在「Maven」概念中,不可能存在,也不允許存在,兩個實際內容不同,卻擁有相同的正式版本號,所以,當「Maven」在編譯專案時,目標專案描述依賴版本為「1.0
」的「Jar」時,其只要確定本地端同樣有一個版本為「1.0
」的「Jar」就可以,它不會再去檢查其簽名,因為在它的觀念中,「1.0
」就只有一種;至於為什麼「Release」必須是「不可變」,詳細內容請參考「Immutability of Published Components」。
但為什麼「Snapshot」版本號相同,卻還要去檢查它的內容呢?
這是因為,「Snapshot」在「Maven」的認知中,是一種增量版本,是用於開發自測階段的版本號,此時的程式碼本來就不穩定,頻繁修改是常態,總能每改一個字就加一個版本號吧?
上一個版本是「1.0
」,下個版本就變成「1.187
」,那中間的版本號呢?
不好意思,開發者測試用掉了,這能聽嗎?
所以「Maven」就提供一種識別符,也就是「-SNAPSHOT
」,其代表目前正處於「增量態」,意思就是:「我還在開發中,每次內容不一樣都正常的」,而其行為的就是:「Lastest」,即前面通通不算,就最近的這個。
說真的,要不是這次的狀況,筆者一直不知道「Snapshot」、「alpha」、「beta」⋯等,都是具有意義的,事實上,「Maven」在版本號的規範並不只有這樣,更多內容可以參考「Understanding Maven Version Numbers」。
最後,筆者補充一下快取機制,其是「Maven」提供一種功能,主要目的就是避免每次都到「Nexus」上撈取重覆的「Library Jar」,這是重要的機制,不然每次編譯都要重新抓取一次「Jar」,別說浪費時間,伺服器也扛不住啊,更別多這要佔用多少傳輸量,所以「快取機制」是必要的,但也不用擔心快取機制誤事,事實上,筆者這次的狀況是自己沒搞懂「Maven」工作原理,與快取機制無關,快取機制也是可以設定「Library Jar」的更新頻率的,只要在其「updatePolicy
」的標籤中配置相對應的參數即可,詳細請參考「Apache Maven Project/Settings」。
三、參考資料
- Apache Maven Project, Maven CLI Options Reference
- Apache Maven Project, Settings
- Maven Central Repository Documentation, Immutability of Published Components
- Oracle Help Center, Fusion Middleware Developing Applications Using Continuous Integration/7. Understanding Maven Version Numbers