Showing posts with label dvcs. Show all posts
Showing posts with label dvcs. Show all posts

2011/04/28

一種應用源碼管理的實作

這是之前 應用系統的版本管理 一文的實作, 目的是開源應用系統的版本管理。嘗試解決應用系統的客製化部份, 除了快速跟隨主要版本的提升, 同時也方便系統的繁殖 ... 預防天災(?)

這裡以 OFBiz r1096871 為例, 檔案有 {java:1436, xml:2626, groovy:454, properties:103, js:451, css:101, jar:391}, 它一直保持在更新的 狀態

面對必要的客製化, 過去常用上游分支 (vender branch) 手法處理, 以固定目錄版本管理為主, 實務上要面對許多細節。現在流行的分散式版本管理, 加上 rebase 大大改善舊有的方式。但分支有版本相依的特性, 而 OFBiz 的版本存檔 (snapshot) 就有 215mb 之譜 ... 要保存和轉移都負擔。

利用補丁管理(Patch Management)是不錯的主意, Mercurial 的 MQ 到 1.8 已經相當成熟。唯一問題是 OFBiz 使用 Apache 官方版本管理 Subversion, 由於版本存檔太大導致 hg-svn 掛點, 不得不生出對策。

因此, 題目變成上游是 svn (原本用 git, 簡化問題還是用 svn 示範), 實際處理用 hg 加 mq。

以自己流通的 mq 版本存檔為例, 大小約 100kb 左右(zip), svn 與對應 hg 則依需要而建。在 hg 功能的協助下, 能快速跟隨 trunk 版本更新, 而 mq 存檔小也利於保存和繁殖。(OFBiz 的 mq 範例放在 bitbucket )

但是實際運作起來, 操作有些瑣碎, 對象也繁多 ... 還好有 Fabric 加持簡化成三種操作, 得以省略一堆指令。

三種操作

  1. 初始 (init)
    拿到 mq 存檔, 加上 OFBiz Apache 上的 zip 檔 (附帶 .svn)。先把 OFBiz zip 檔解開有了 svn 儲存庫 (repository), 再更新與 mq 相同的 svn rev. 版本, 接著複製到 hg 儲存庫。

  2. 更新 (update)
    因應上游 trunk 更新, 把 svn 儲存庫與上游同步, 再更新 hg 儲存庫, 最後把 mq 做 rebase 處理。需要留意 OFBiz 會更新 *jar, 是不在 svn diff 服務範圍, 得由外部 diff 助拳。而後, 視情況對 mq 進行提交 (mqcommit), 或上送(mqpush)。

  3. mq變更 (mqpull)
    上游版本變更與驗證已完成, 並上送 bitbucket (第二個操作)。這時要對運作端進行更新, 先把新的 mq 取下, 依據新 mq 更新 svn 儲存庫, 同時更新對應 hg 儲存庫。


一般使用只要用 初始mq變更 就能保持版本換新, 僅在開發與測試需要 更新(mq commit/push) 的機制。

常見方式是: 只有開發與測試使用版本管理工具, 運作端用 rsync 之類工具同步, 符合標準 開發,
測試, 正式環境 分階段的開發週期。

用 svn-hg-mq 的好處是可從容面對應用系統會遇到的因地制宜 ... 如果不時在 正式環境 被要求改一點顯示文字或邏輯, 用此法就放手去改。然後以 mq commit 保存差異並列管, 之後再看看要怎麼處理, 同時也可放心進行上游版本更新(如果有的話)。

假使開發語言單一, 系統規模在一定範圍之內, 人力還是可以處理。而像 OFBiz 有 java, xml, groovy, ... 子系統多, 關連又盤根錯節, 就最好有固定的方式處理。

實際操作

在 OS X 10.6 + macports 與 Ubuntu 10.10 測試過。

要有 gcc, python, virtualenv, apple/sun jdk 6 (openjdk 6 還是無法使用 OFBiz)。因為 mercurial 1.8 和 fabric 1.0.1 都要用新版, 習慣用 virtualenv 來建, 這時得有 gcc 相關工具備著。


環境建立:
@ /w/proj
$ virutalenv --no-site-packages fab
$ cd fab
$ source bin/activate
(fab) $ pip install mercurial
...
(fab) $ pip install fabric
...
(fab) $ curl -O https://bitbucket.org/tcc/ofbiz-util/raw/38e378fef189/fabfile.py


準備 ~/.hgrc:
[ui]
username = jia@side.org
[extensions]
pager = 
hgext.mq = 
[pager]
pager = LESS='FSRX' less

注意 username, pager, mq 都要有 ... fabfile.py 會用到。


執行:
假設主要目錄是 /w/proj/fab/ofbiz, 會在下面自動建立 svn/, hg/, zip/ 三個目錄 ...
  1. svn/ 目錄為 svn 儲存庫, 和遠端 svn 同步。
  2. hg/ 目錄為 hg 儲存庫, 用來執行應用系統。和本地 svn/ 同步, 並且以 hg-mq 進行客製化處理。
  3. zip/ 目錄存放 ofbiz-trunk-current.zip 檔案, 只在 "初始" 步驟有用, 之後可刪除。
  • 進行初始儲存庫:
    @ /w/proj/fab
    $ source bin/activate
    (fab) $ fab init:/w/proj/fab/ofbiz
    ...
    就會進行, 由於部份操作挺費時間, 或須備妥網路, 像下載 ofbiz-trunk-current.zip 會停下來詢問, 若要執行到底:
    (fab) $ fab init:/w/proj/fab/ofbiz,auto=Y
    ...
    加上參數 auto=Y 即可。
  • 當 mq@bitbucket 有新版本要更新:
    (fab) $ fab mqpull:/w/proj/fab/ofbiz,sync=Y
    ...
    若沒有 sync=Y 就得手動進行 sync:
    (fab) $ fab sync:/w/proj/fab/ofbiz
    ...
目前 mq@bitbucket 沒有開放更新, 得自行建立 mq repository ... 修改 fabfile.py:
  1. 註解第 31 列 hg_mq_url = 'https://bitbucket.org/tcc/ofbiz-mq'。
  2. 打開第 32 列 hg_mq_url = 'file:///w/proj/ofbiz/mq2' 做必要修改。
然後與 trunk@OFBiz 同步:
(fab) $ fab update:/w/proj/fab/ofbiz
...
接著提交寫入, 並上送:
(fab) $ fab mqcommit:/w/proj/fab/ofbiz,push=Y
...
若沒下 push=Y, 要手動進行上送:
(fab) $ fab mqpush:/w/proj/fab/ofbiz
...

全部操作就這樣了。

建置 OFBiz 前先置入 hg-mq:
@/w/proj/fab/ofbiz/hg
(fab) $ hg qpush -a
...

然後按 OFBiz 安裝進行:
(fab) $ ./ant clean-all
...
(fab) $ ./ant run-install-extseed
...
(fab) $ ./ant create-admin-user-login
...
(fab) $ ./startofbiz.sh

...

如果要建立另一份系統做開發, 把目錄 /w/proj/fab/ofbiz/ 改為其他的, 例如 /w/proj/yi/side/ ... 依據客製 hg-mq 很快就能建立一套新的應用系統。不用辛辛苦苦裝完系統, 再逐一做客製相關設定。

試試看, 或許有更好的做法 :)

2010/11/08

應用系統的版本管理

一段歷史

經過幾年軟體版本管理工具爆炸性的競合, 如果不考慮控制的問題, 我們可以自由選用 subversion(svn), mercurial(hg) 以及 git。svn 存在時間最早, 在有一些歷史的專案和中小公司比較常見; hg 由於簡單明確, 大型源碼社群如 sourceforge, codeplex, google code 普遍接受; 而 git 強大穩定, 則是技客首選。

這三者陸續在用, 雖然深淺各有不同, 從歷史角度來看: 最早通用的 cvs 是管理檔案版本, 所以會有 v1.0, v1.1, v1.2, ….。svn 側重整個專案版本, 標示版本以整個儲存庫為準 r123, r223, r323, … (r=revision)。大概是 cvs 時期一個檔案就可做完一件事, 到了 svn 得靠一堆檔案 … 請參考『軟體工程師的進化』一文。

到了 dvcs(hg, git) 回歸功能面管理, 版本像是 229a46966a72, 13a7d2806d50, 64d88c389b67, … 的 uuid。意義不再是檔案版本編號(v1.0), 或由儲存庫變更序號(r123)做認定。應該沒有人可從 dvcs 裡版本號碼看出任何意義。一方面在每次提交版本(commit)輔以適當註解, 而整體來看, 端視儲存庫擁有者認定版本的意義。因為是 dvcs … 只要有心, 人人都可以是儲存庫擁有者 … peace!

對一個專案來說, 針對檔案變更追蹤是過小, 像流行的 MVC (Model-View-Controller)在概念上就跨了三層。若針對儲存庫變更追蹤又太大, 變更頻繁的話, revision 就如同雪片般, 好看是好看, 但一般也只是好看。因此, dvcs 當是比較符合趨勢的選擇。

一種問題

交待完歷史, 回頭看主題。假設專案有源碼要管理, 這個應用系統是承上啟下, 也就是依據上游源碼為基礎, 提供下游客戶穩定、加強、或客製的版本。是種常見模型:
上游儲存庫 UR - Upstream Repository
我的儲存庫 MR - My Repositroy

源碼儲存庫畢竟是技術人員才會接觸到, 大部分狀況以策略解決: 有時將上游源碼設定為程式庫, 簡化問題; 或以廠商分枝(Vendor Braches)將上游源碼納入專案; 而產品類專案, 可忽略 n 個客戶問題。

但如果使用開放源碼 CMS, CRM 或 ERP 這類應用為主的系統, 上游持續加強版本, 下游因應需求不斷變更 … 會爆炸的點大概就是位居中間的儲存庫。
在網路上, 看到相關問題討論有:
  1. 非官方 hg 流程 中 Offsite working on dynamic websites。其中『Upgrading Drupal with Git』(部落格已落幕, 可參考 archive.org 資料)。
  2. 也是 drupal 開發, 教學視訊 Git With Drupal 7
  3. opentaps(ERP) 的建議 Managing Customizations with Upgrades, 大抵是分為四個層次: a. 瞭解現有的, 將異動最小化 b. 貢獻源碼, 免除版本差異, 透過社群改進 c. 獨立應用程式在特定目錄下 d. 若修改核心及社群不接受的源碼, 得注意合併問題。最末項不建議, 因容易出包。
上面第三項 "在特定目錄下" 放應用程式, 是大部分應用系統普遍的做法, 像 SugarCRM 也有類似的機制。

通常專案會設定上游的版本, 但長期提供服務, 必然會面對上游版本變更。而下游的需求, 投入行業時間夠久, 一般也會有共通方式解決, 例如提供一個檔案上傳或流程整合的功能, 應該也不希望一家處理過版本提升後, 還得要處理其他家客戶相同問題。

技術上, 沒有通用做法的話, 能抑制爆炸的源碼就大概只有爆炸的肝。(以爆制爆?)

一項解法

如果著眼於儲存庫, 所有問題都成為一團。首先區分上游變更, 與下游需求這兩個變動的來源。其他像共通功能的開發, 問題的修正, … 先納入下游需求。

再來, 如版本管理的演化(csv → svn → dvcs), 因時制宜般, 上游變更以分枝(branches)處理, 下游需求以變更佇列(patchs queue)對付, 再建立一個儲存庫負責照拂需求的變更佇列, 是為『變更儲存庫』。
變更儲存庫PR - Patch Repository
變更佇列在 git 中有外掛 stgit, quilt, 至於 hg 是內建 mq (mercurial queues)。

在手上實際 ERP 案例中, 上游儲存庫每個月大約有 800kb 左右的差異檔(diff), 而變更佇列除了中文化有 5mb 的差異檔, 其他加起來不到 300kb。每月同步上游儲存庫後, 再更新變更佇列, 也就是 UR 與 PR 會有版本對應的關係。只要 UR 與 PR 同步後, 就可一體適用其他相同系統的客戶。節省下的資源可專注差異化部份。

一方面是變更的大小與不同機制的搭配, 另一方面是變更佇列對每項修補差異的處理, 比分枝來得直覺。例如把中文化和其他修補一起放入同一個分枝中, 當上游版本更新, 要合併到分枝時, 假使自動合併無效, 要看到檔案才能處理, 同時可能也要處理 API 差異之類的。簡單來說, 就是要有經驗的工程師才能處理版本合併的事務。如果是以變更佇列處理的規劃, 就很方便分工, 中文化部份安排通中/英文, 細心的工程助理打點即可。

參考