Nant daily build實踐
折騰了一個周,基於Nant的VS.NET項目每日構建終於成功了,在網上實際上有很多這樣的例子,但所整合的解決方案都比較簡單,我現在做的解決方案,有4個類庫項目和2個web項目,使用網上的資料時,編譯過程都不能成功,最後翻了不少資料,問了不少人才成功,其中主要過程參考了ht
tp://bitarray.co.uk/marc/這個部落格,我的文章大部分內容就算是翻譯吧,寫出來希望對大家有用處,同時感謝給過協助的朋友,包括coolbug的文章
asp.net下開發產品的部署,不同於asp中interdev開發目錄和網站發布目錄相同的特點,在asp.net下,開發目錄實際上存在於VSS中,必須建立對應的發布目錄,將編譯過的檔案放到其中,建立應用程式的虛擬目錄才能完成asp.net的發布,如果使用手動方式完成這一過程,則在項目多、
或者迭代式開發的多次小規模發布情況下,人工工作量大,且不易管理,本文的.net每日構建過程,可以自動的完成這一過程,簡化管理過程。
一、 系統需求
Win2000 以上作業系統 .NET 架構1.0以上運行平台
二、 相關工具及
1. Nant 開源工具,完成.net代碼自動編譯及其它工具的調用(必選)
http://nant.sourceforge.net/builds/
2. NantContrib 開源工具,Nant的向外延展群組件,實現Nant從VSS中擷取檔案功能(必選)
http://nantcontrib.sourceforge.net/nightly/builds/
3. Visual Source Safe 原始程式碼控制工具,VSS6.0C以上版本(必選)
三. 相關軟體安裝
1. 安裝VSS
在指定伺服器上安裝VSS6.0C以上版本
2. 安裝NANT和NantContrib
在網上下載最新版NANT和NantContrib壓縮包
解壓Nant,複製其中bin目錄到安裝地點,如d:\DailyBuildTools\Nant\bin
解壓NantContrib,複製其中bin中所有內容到Nant的bin目錄
註:如果Nant和NantContrib版本不相容,需要重新編譯Nant,可下載原始碼後查看相關文檔
四. 建立.Net項目,並簽入到VSS中指定位置,分配相應帳號(除相關開發和管理員外,可為Nant分配專用帳號)
五. 定義項目發布所包含的過程
Nant工具功能強大,配合nant、ndoc、fxcop等工具,可以完成代碼編譯,網站發布,文檔產生、代碼測試等功能,並可反向在VSS中加入基準。
一個比較通用的發布過程如下:
5.1 定義發布環境,如網站發布目錄,原始碼存放目錄、文檔存放目錄、發布版本分發包存放目錄等
5.2 項目組人員編寫Nant所需要的運行檔案default.build,並根據環境定義其中的相關變數,並提交到伺服器
5.3伺服器的計劃任務等自動過程每天定時運行Nant
5.4 Nant清除上一版本發布時存放的文檔
5.5 Nant從VSS中擷取新的原始碼
5.6 Nant編譯從VSS中擷取的原始碼,並將產生的dll檔案存放到指定目錄
5.8 產生測試報告
5.9 產生項目文檔(需要原始碼中加入充分的注釋)
5.10 如果包含web項目,Nant刪除不必要的如*.cs等檔案後,將發布需要的*.aspx、*.resx、*.js等檔案按原有目錄結構複製到指定目錄
5.11 向VSS中加入基準定義(需要寫入權限)
5.12 產生項目zip分發包,並用指定格式定義分發包檔案名稱,存到指定目錄
5.13 發布項目到指定目錄,如web網站將發布到指定虛擬目錄,也可由Nant來完成虛擬目錄的建立過程
六. 編寫Nant運行所需要的運行檔案default.build
此檔案要求在同一目錄下有且僅有一個存在,所以為統一管理,所有項目可以統一使用一個default.build檔案
該檔案中將包含所有相關項目的vss帳戶資訊,需要保證其安全
將些檔案放在獨立的目錄中以便於管理
七. Default.build的編寫說明
根據上面描述的通用項目發布過程,一個基於Nant的項目發布過程主要為以下幾步:
1 清除舊版本
2 擷取新版代碼
3 編譯
4 測試和文檔產生
5 發布
因為Nant在同一目錄下僅可有一個default.build的限制,可將每個項目的發布流程定義為4個xml檔案,如對集團黃頁系統
TYS.Master.xml build主檔案,定義各種環境變數
TYS.PreBuild.xml 項目預先處理流程定義檔案,完成舊的檔案的清除工作
TYS.Build.xml 主要構建流程定義檔案,完成VSS中擷取,編譯、產生壓縮包的過程,並測試、產生文檔的過程
TYS.Deploy.xml 項目發布到最終位置的流程定義檔案
對於複雜的多層的項目,build文檔的編寫比較複雜,可以參考Nant和NantContrib的相關文檔,針對不同類型的項目,xxx.Build.xml檔案的編寫可以使用不同方法,對於簡單項目,可以使用Nant的solution任務、對於複雜的項目,可以使用NantContrib的slingshot任務自動成生,不過可
能需要做稍微的改動,
在合理的定義變數的條件下,上面4個檔案中的XXX.PreBuild.xml、XXX.Build.xml、XXX.Deploy.xml實際上都是可以重用的,也就是說,每次項目發布,項目組人員只需要提交XXX.Master.xml
八. Default.build的編寫過程
下面以集團黃頁系統TYS的default.build編寫為例講述具體過程,先假定項目發行伺服器Nant配置完成,並存在以下結構的目錄設定
1) 項目所在vss目錄:vss.directory = d:\VSS\TYS
2) 項目構建工作和存放檔案根目錄:core.basedir = d:\Daily_Build_Folder
3) 項目虛擬目錄位置:core.publish = d:\inetpub\wwwroot\tys
4) 所有項目原始碼存放目錄:Core.build = Core.basedir\source
5) 項目zip分發包存放目錄core.distribution = core.basedir\distribution
6) 項目日誌存放目錄core.logs = core.basedir\logs
7) 項目文檔存放目錄core.documentation = core.basedir\docs
1編寫XXX.Master.xml
<?xml version="1.0" encoding="gb2312"?>
<project name="DNMCCIT.TYS" default="go">
<!-- 可配置屬性列表 -->
<!-- 主要屬性列表 -->
<property name="core.basedir" value="d:Daily_Build_Folder"/>
<property name="core.publish" value="D:inetpubwwwrootTYS"/>
<property name="core.build" value="Build"/>
<property name="core.distribution" value="Distribution"/>
<property name="core.logs" value="Logs"/>
<property name="core.documentation" value="Docs"/>
<property name="core.source" value="Source"/>
<property name="supportal.core" value="\SupportalServersupportal$files"/>
<property name="supportal.fxcop" value="${supportal.core}fxcop"/>
<property name="supportal.nunit" value="${supportal.core}nunit"/>
<property name="supportal.ndoc" value="${supportal.core}ndoc"/>
<!--項目相關資訊-->
<property name="project.manager" value="p520214@gnpjvc.com.cn"/>
<property name="project.developer" value="p520214@gnpjvc.com.cn"/>
<property name="project.name" value="DNMCCIT.TYS"/>
<!--Build位置資訊-->
<property name="build.configuration" value="Release"/>
<property name="build.number" value="1.0"/>
<property name="build.directory" value="${core.basedir}${core.build}${project.name}"/>
<property name="build.documentation" value="${build.directory}${core.documentation}"/>
<property name="build.logs" value="${build.directory}${core.logs}"/>
<property name="build.support" value="${core.publish}@support"/>
<property name="build.distribution" value="${core.basedir}${core.distribution}${project.name}"/>
<property name="build.publish" value="${core.publish}${project.name}"/>
<!--VSS資訊-->
<property name="vss.username" value=""/>
<property name="vss.password" value=""/>
<property name="vss.dbpath" value="\t-net1vss8002TYSsrcsafe.ini"/>
<property name="vss.path" value="$/DNMCCIT.TYS/"/>
<property name="vss.directory" value="${core.basedir}${core.source}${project.name}"/>
<!--VS.NET解決方案資訊-->
<property name="solution.file" value="DNMCCIT.TYS.sln"/>
<!--程式集引用資訊-->
<property name="solution.assembly.1" value="DNMCCIT.TYS.Data"/>
<property name="solution.assembly.2" value="DNMCCIT.TYS.Entity"/>
<property name="solution.assembly.3" value="DNMCCIT.TYS.Logic"/>
<property name="solution.assembly.4" value="DNMCCIT.TYS.Common"/>
<property name="solution.fxcop" value="${project.name}.fxcop"/>
<!--屬性列表結束-->
<target name="go" depends="prebuild,build"/>
<target name="prebuild">
<nant buildfile="TYS.PreBuild.xml" target="go" inheritall="true"/>
</target>
<target name="build">
<nant buildfile="TYS.Build.xml" target="go" inheritall="true"/>
</target>
<target name="deploy">
<nant buildfile="TYS.Deploy.xml" target="go" inheritall="true"/>
</target>
</project>
2編寫TYS.PreBuild.xml
<?xml version="1.0" encoding="GB2312"?>
<project name="TYS.PreBuild" default="go">
<target name="go" depends="clean,getsourcecode"/>
<target name="clean" description="Remove all files">
<delete dir="${build.directory}" failonerror="false"/>
<delete dir="${vss.directory}" failonerror="false"/>
<delete dir="${build.distribution}" failonerror="false"/>
<mkdir dir="${build.directory}" />
<mkdir dir="${build.documentation}" failonerror="false" />
<mkdir dir="${build.logs}" failonerror="false" />
<mkdir dir="${vss.directory}"/>
<mkdir dir="${build.publish}" failonerror="false" />
</target>
<!--從VSS中取得原始碼-->
<target name="getsourcecode">
<vssget
user="${vss.username}"
password="${vss.password}"
localpath="${vss.directory}"
recursive="true"
replace="true"
dbpath="${vss.dbpath}"
path="${vss.path}"
/>
</target>
</project>
這個檔案實際上是通用的,對所有項目基本相同
3 編寫TYS.Build.xml檔案,TYS.Build.xml檔案根據項目複雜程度不同而不同,下面是一個只有一層,且為web層的例子
<?xml version="1.0" encoding="GB2312"?>
<project name="TYS.Build" default="go">
<target name="go" depends="compile,distribute"/>
<!--編譯解決方案-->
<target name="compile" >
<!--
如果項目比較簡單,如只有一層或層之間關係比較簡單,可以直接使用項目的解決方案檔案
-->
<solution
solutionfile="${vss.directory}${solution.file}"
configuration="${build.configuration}"
outputdir="${build.directory}">
<webmap>
<map
url="http://localhost/DNMCCIT.TYS.WEB/DNMCCIT.TYS.WEB.csproj
path="${vss.directory}TYS.WEBDNMCCIT.TYS.WEB.csproj"
/>
</webmap>
</solution>
</target>
<!--產生分發包-->
<target name="distribute">
<IMG src="/Images/OutliningIndicators/None.gif" align=top<?xml version="1.0" encoding="GB2312"?>
<project name="TYS.Deploy" default="go">
<target name="go" depends="propertycheck,unzip,copyrequiredfiles"/>
<!--檢查必須的參數是否設定-->
<target name="propertycheck" >
<ifnot propertyexists="build.number">
<fail message="build.number 屬性沒有設定,不能進行部署" />
</ifnot>
<ifnot propertyexists="target.directory">
<fail message="target.directory 目錄沒有設定,不能進行檔案複製" />
</ifnot>
</target>
<target name="unzip">
<unzip
zipfile="${build.publish}${project.name}--build-${build.number}.zip"
todir="${target.directory}" />
</target>
<target name="copyrequirefiles">
<mkdir dir="${target.directory}webctrl_client" failonerror="false" />
<mkdir dir="${target.directory}aspnet_client" failonerror="false" />
<!--複製web assets-->
<copy todir="${target.directory}webctrl_client">
<fileset basedir="${core.publish}webctrl_client" >
<includes name="**"/>
</fileset>
</copy>
<copy todir="${target.directory}aspnet_client">
<fileset basedir="${core.publish}aspnet_client" >
<includes name="**"/>
</fileset>
</copy>
</target>
</project>
本例中,並沒有給出產生文檔、日誌等功能的build代碼,實際上這個過程應該寫在TYS.Build.xml中,具體運用中,項目成員可根據需要參照相關文檔添加
三. 自動運行過程
編寫全域使用的default.build,存放到buildconfig,在其中調用TYS.Master.xml,內容如下:
<?xml version="1.0" encoding="gb2312"?>
<project name="DNMCCIT" default="go">
<target name="go" depends="DNMCCIT.TYS"/>
<target name="DNMCCIT.TYS">
<nant buildfile="TYS.Master.xml" target="go" inheritall="true"/>
</target>
</project>
編寫批次程式,內容可以簡單的只有一行
Nant,然後存放到d:\buildconfig下的makefile.bat中
在計劃任務中增加自動運行計劃,按自定計劃運行makefile.bat
四. 其它解決方案
支援.net平台、VSS做原始程式碼控制的每日構建,除了本文提到的Nant之外,還有微軟公司的BuildIt和MsBuild兩種工具,BuildIt工具因為擴充功能不強,使用較少,MSBuild的開發借鑒了Nant的思想,很多方面使用方法和Nant類似,並且也使用xml格式的build定義檔案,並且可以根據VS.NE
T的解決方案sln檔案完全自動進行build,網上資料也較為豐富,但MSBuild現在階段為beta產品,僅在Visual Studio2005中提供,且需要.net framework2.0的支援,.net1.1下的產品只有有限有人員可以得到,待MSBuild正式發布後,將會成為另一有力工具
五. 用於管理方便的部分自動發布方式
上面講述的方法,有一個比較大的缺點,就是對於不熟悉nant的人來說,編寫build檔案有一定難度,實際上如果僅從伺服器管理方便的角度,完全可以使用一種比較簡便的方法,描述如下:
a) 每個具體的開發項目,在其VSS中建立BUILD項目,如對黃頁系統為
$DNMCCIT.TYS.BUILD
b) 由開發人員,不定期從開發環境,將編譯好的build檔案,以zip壓縮包的方式存放到$DNMCCIT.TYS.BUILD中
c) 修改伺服器的nant編譯定義檔案TYS.DeployZip.xml,過程僅為從VSS中取得DNMCCIT.TYS.BUILD.zip,並在伺服器上解壓並釋放到指定的目的目錄
此時的default.build將只調用TYS.DeployZip.xml即可,代碼如下:
Default.build
<?xml version="1.0" encoding="gb2312"?>
<project name="DNMCCIT" default="go">
<target name="go" depends="DNMCCIT.TYS"/>
<target name="DNMCCIT.TYS">
<nant buildfile="TYS.DeployZip.xml" target="go" inheritall="true"/>
</target>
</project>
TYS.DeployZip.xml
<?xml version="1.0" encoding="GB2312"?>
<project name="TYS.Deploy" default="go">
<property name="core.basedir" value="d:\Daily_Build_Folder\"/>
<property name="core.publish" value="D:\inetpub\wwwroot\TYS\"/>
<property name="core.build" value="Build\"/>
<!--項目相關資訊-->
<property name="project.manager" value=""/>
<property name="project.developer" value=""/>
<property name="project.name" value="DNMCCIT.TYS"/>
<!--Build位置資訊-->
<property name="build.directory" value="${core.basedir}${core.build}${project.name}\"/>
<!--VSS資訊-->
<property name="vss.username" value="aaaa"/>
<property name="vss.password" value=""/>
<property name="vss.dbpath" value="\\t-net1\vss\8002TYS\srcsafe.ini"/>
<property name="vss.path" value="$/DNMCCIT.TYS.BUILD/"/>
<property name="vss.directory" value="D:\inetpub\wwwroot\TYS\"/>
<target name="go" depends="clean,getzip,unzip"/>
<target name="clean" description="Remove all files">
<delete failonerror="false">
<fileset>
<include name="${core.publish}**" />
<include name="${build.directory}" />
</fileset>
</delete>
</target>
<!--從VSS中取得原始碼-->
<target name="getzip">
<vssget
user="${vss.username}"
password="${vss.password}"
localpath="${build.directory}"
recursive="true"
replace="true"
dbpath="${vss.dbpath}"
path="${vss.path}"
/>
</target>
<target name="unzip">
<unzip
zipfile="${build.directory}${project.name}.BUILD.zip"
todir="${core.publish}" />
</target>
<target name="copyrequirefiles">
<mkdir dir="${target.directory}webctrl_client" failonerror="false" />
<mkdir dir="${target.directory}aspnet_client" failonerror="false" />
<!--複製web assets-->
<copy todir="${target.directory}webctrl_client">
<fileset basedir="${core.publish}\webctrl_client\" >
<includes name="**"/>
</fileset>
</copy>
<copy todir="${target.directory}aspnet_client">
<fileset basedir="${core.publish}\aspnet_client\" >
<includes name="**"/>
</fileset>
</copy>
</target>
</project>
註:不知怎麼弄的,上次發的內容沒發完,xml內容怎麼變成圖片了?現在補上