概述
对于正在开发的项目,一般需要搭建测试/演示环境,将开发中的最新程序部署到测试环境上(Edge版本,一般指最新成果,但未经过严格测试),便于公司领导、客户、项目经理及时了解开发进展。
最简单的部署也大致需要获取源代码、编译、部署、运行几个环节,这几个环节伴随着较多的“手工操作”,容易出错,并且部署次数越多,累计花费的时间也越多。
因此在java领域,出现了很多项目构建(build)工具,而.net平台上的构建工具数量较少,除了微软自己的MSBuild外,最有影响力的是nant,NHibernate、log4net等多个开源项目都使用它来构建。
网络上介绍nant、svn的文档相当多,各位可以自行学习,本文的目的是介绍通过组合使用这些工具,用以完成“获取源代码”、“编译”、“部署”、“运行”几个环节的操作,将部署过程标准化,减少部署出错概率、节省时间。
个人认为mono是.net的未来,但我对linux还不熟悉,所以只讨论windows平台下nant的应用。
打造自己的工具包
想要达到“一键部署”的效果,需要制作工具包,将多个工具配合使用才可以,工具包的好处是我们可以根据项目情况对工具包进行剪裁和完善,删除不必要的文档和文件,以控制工具包尺寸,另外工具包要尽可能与环境无关,不应该要求运行环境安装svn、nant等等,我们应该包含这些基础组件,达到开箱即用的效果。因此需要大家自己动手了。我们的工具包中有三个工具,分别是svn客户端、nant和nantcontrib,另外还需要.net framework 2.0或以上版本。
1.建立工具包文件夹
首先在c盘根目录建立文件夹OneClickDeploy作为工具包的根文件夹,这个路径可以根据情况自行修改,构建脚本中指定即可。
2.svn客户端
svn客户端用于从svn服务器获取源代码,svn客户端的windows下二进制分发包有很多,建议使用
SlikSvn,下载安装后,我们需要将C:\Program Files\SlikSvn\bin下的所有文件复制到c:\OneClickDeploy\SlikSvn下,只需要复制bin文件夹即可,其他文件夹中是版权声明、帮助文件等内容,我们不使用他们。
3.nant程序
nant就是我们构建工具的核心了,它负责执行构建脚本,并提供了很多任务、函数,比如获取当前时间,操作文件等,详细文档参见
http://nant.sourceforge.net/release/latest/help/。
从
http://nant.sourceforge.net/nightly/latest/nant-bin.zip下载最新程序,目前发布的0.86 Beta 1版本存在bug,因此还是建议下载nightly程序。下载后,将压缩包中的bin文件夹下的所有文件复制到c:\OneClickDeploy\nant-0.86。
4.nantcontrib程序
nantcontrib是对nant任务的扩展,比如支持多种源代码版本库,对iis操作等,文档参见
http://nantcontrib.sourceforge.net/release/latest/help/。
从
http://nantcontrib.sourceforge.net/nightly/latest/nantcontrib-bin.zip下载最新程序,下载后,将压缩包中的bin文件夹下的所有文件复制到c:\OneClickDeploy\nantcontrib-0.86。
至此,我们的工具包准备完成,大家可以自行丰富工具包,比如添加7z,提供文件压缩打包功能,文件夹结构如下:
c:\OneClickDeploy
\nant-0.86
\NAnt.exe
\...
\nantcontrib-0.86
\NAnt.Contrib.Tasks.dll
\...
\SlikSvn
\svn.exe
\...
一般web站点的部署脚本
<?xml version="1.0" encoding="utf-8" ?>
<!-- 项目名称,只用于显示 -->
<project name="project_name" >
<!-- 使用NAnt.Contrib,指定Contrib的路径 -->
<loadtasks assembly="C:/OneClickDeploy/nantcontrib-0.86/NAnt.Contrib.Tasks.dll" />
<!-- 源代码环境配置 -->
<!-- 源代码输出路径,默认为输出到当前文件夹下的src文件夹 -->
<property name="source.outdir" value="src"/>
<!-- svn服务器地址 -->
<property name="source.svn.path" value="svn://server/trunk/web0"/>
<!-- svn用户名 -->
<property name="source.svn.username" value="user"/>
<!-- svn密码 -->
<property name="source.svn.password" value="pass"/>
<!--
***************************************************************************
更新代码:如果代码已签出,执行update命令,否则执行checkout命令,一般不需要修改
***************************************************************************
-->
<target name="source">
<if test="${directory::exists(source.outdir)}">
<echo message="更新代码${source.svn.path}"/>
<svn-update uri="${source.svn.path}" destination="${source.outdir}" username="${source.svn.username}" password="${source.svn.password}" verbose="false" />
</if>
<if test="${not directory::exists(source.outdir)}">
<echo message="签出代码${source.svn.path}"/>
<mkdir dir="${source.outdir}"/>
<svn-checkout uri="${source.svn.path}" destination="${source.outdir}" username="${source.svn.username}" password="${source.svn.password}" verbose="false" cache-auth="false" />
</if>
</target>
<!--
编译代码“函数”
compile.target:exe, winexe, library or module
compile.assembly: 程序集
compile.outdir: 输出路径
complie.debug: true/false
-->
<target name="compile">
<!-- 生成编译后的文件名 -->
<if test="${compile.target=='library'}">
<property name="compile.output" value="${compile.assembly}.dll"/>
</if>
<if test="${compile.target=='winexe'}">
<property name="compile.output" value="${compile.assembly}.exe"/>
</if>
<csc target="${compile.target}" output="${path::combine(compile.outdir, compile.output)}" debug="${compile.debug}" optimize="true" verbose="false">
<sources>
<!-- 编译所有cs文件 -->
<include name="${source.outdir}/${compile.assembly}/**/*.cs" />
</sources>
<references>
<!-- 编译引用的dll文件,我将依赖的dll放到了svn://server/trunk/web0/Assembly文件夹下,所以这里需要制定引用Assembly下的所有dll文件 -->
<include name="${source.outdir}/Assembly/**/*.dll" />
<!-- 引用编译结果文件夹下的所有dll文件,一般来说类库项目之间也存在应用关系,基础类库编译完成后将编译结果dll存放compile.outdir文件夹下,编译业务类库时需要引用基础类库,因此需要引用compile.outdir下的所有dll -->
<include name="${compile.outdir}/**/*.dll" />
<!-- 排除引用的dll文件 -->
<exclude name="${source.outdir}/Assembly/**/libmySQL.dll"/>
</references>
<resources>
<!-- 嵌入的资源,需要将nhibernate映射文件作为嵌入的资源,编译到程序集中 -->
<include name="${source.outdir}/${compile.assembly}/**/*.hbm.xml" />
</resources>
</csc>
</target>
<!--
***************************************************************************
构建web站点
***************************************************************************
compile.debug:
/debug [+/-] :当选用/debug +就会把创建.pdb文件,并把调试信息存储到里面;/debug -是一个缺省设置,就是不产生任何调试信息。
/debug:[full/pdbonly] :当使用/debug:full就是创建缺省的调试信息,有点类似/debug+选项。/debug: pdbonly选项是创建.pdb文件,并且你只能使用源代码调试在调试工具里。
compile.outdir:
编译结果输出文件夹
-->
<property name="compile.debug" value="PdbOnly"/>
<!-- 将类库的编译结果存放到web/bin文件夹下 -->
<property name="compile.outdir" value="${source.outdir}/web/Bin"/>
<!-- web站点的物理路径 -->
<property name="deploy.rundir" value="C:/web0"/>
<target name="compile-web" description="编译web站点">
<echo message="编译web站点"/>
<!-- 将Common和Business编译后放到源代码web/bin文件夹,需要根据web站点引用类库的依赖关系按顺序编译-->
<delete dir="${compile.outdir}" verbose="true" if="${directory::exists(compile.outdir)}" />
<mkdir dir="${compile.outdir}"/>
<property name="compile.assembly" value="Common"/>
<property name="compile.target" value="library"/>
<call target="compile" />
<property name="compile.assembly" value="Business"/>
<property name="compile.target" value="library"/>
<call target="compile" />
<!-- 源代码Assembly下的所有dll复制到web/bin文件夹-->
<copy todir="${compile.outdir}">
<fileset basedir="${source.outdir}/Assembly">
<include name="/**/*.dll" />
</fileset>
</copy>
<!--停止IIS-->
<exec program="iisreset">
<arg value="-stop" />
</exec>
<!--删除web站点物理文件夹-->
<delete dir="${deploy.rundir}" verbose="true" if="${directory::exists(deploy.rundir)}" />
<!--web预编译-->
<exec program="aspnet_compiler">
<arg value="-v" />
<arg value="/s" />
<arg value="-p" />
<arg value="${source.outdir}/web" />
<arg value="${deploy.rundir}" />
</exec>
<!--启动IIS-->
<exec program="iisreset">
<arg value="-start" />
</exec>
</target>
</project>
上面的构建文件比较复杂,实际上分为几个环节:获取源代码source、编译web站点compile-web,编译web站点内又包括编译类库和预编译web站点,大家需要根据项目的实际情况,对构建文件进行修改。
使用
将上面的构建文件保存在任意文件夹下,如c:\deploy\web.build,并在这个文件夹下创建批处理脚本deploy.bat
path=C:\OneClickDeploy\nant-0.86;C:\OneClickDeploy\SlikSvn;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
nant -t:net-3.5 -buildfile:web.build source compile-web
pause
运行deploy.bat,首先会设置nant、svn、.net2.0 framework的路径,然后使用构建脚本进行部署:
1.更新源代码,首次运行会签出源代码
2.编译类库,示例中为Common、Business
4.停止iis并删除web站点物理路径
3.预编译web站点并输出到web站点物理路径
4.启动iis
本文介绍了使用svn客户端、nant工具,完成“获取源代码”、“编译”、“部署”、“运行”几个环节的操作,抛砖引玉,希望大家灵活运用,提出更好的想法。
下篇文章《nant的典型应用——部署windows服务》