公司最近准备分出一套人马去客户那里做驻场研发,这就涉及到代码库的统一管理。鉴于VSS在互联网上表现不好,而TFS配置又过于复杂,我们选择了SVN。考虑到代码的安全性及性能,我们决定在本地布署自己的SVN服务器,然后每个人安装TortoiseSVN作为客户端,并安装AnkhSVN插件配合Visual Studio使用,安装CruiseControl.NET进行自动化的每日构建,安装Redmine进行项目管理,安装BugFree进行bug管理。
1.VisualSVN,TortoiseSVN,AnkhSVN
我安装的版本是:VisualSVN2.1.9,TortoiseSVN-1.6.16.21511,AnkhSvn-2.1.10129。他们的安装过程都是傻瓜式的,无需多表,主要是安装好后需要进行几个位置的配置。
a).采用Http还是Https
这个选择的优缺点就不多表,其实是看具体的需要。
b).安全证书问题
安装完成后,如果采用Https方式,默认访问时会出现安全证书问题。这其实是因为默认使用的是“自签名证书”。安全证书的原理,简单来讲就是采用两个大素数A和B,用A加密的只能用B解密,用B加密的只能用A解密,此时称A为密钥,B为公钥或者B为密钥,A为公钥。这是一种典型的不对称加密。如果这对钥匙的提供者是公认的认证机构(如Verisign),那么各种主流浏览器就会认为它是安全的证书,反之就是非安全证书。显然,所谓“自签名证书”,就是指这个钥匙的提供者就是自己,在浏览器看来这当然就是典型的非安全证书了,会在最开始的页面显示一个警告。要想去除这个警告,有两种方法,第一种当然就是把你的相关信息发给那些认证机构,让他们帮你生成安全证书。不过这个方案是需要花点银子滴。第二种方法就是手工把当前的“自签名证书”加入到受信任列表里去。这种方法简单易行,具体的方法与说明请自行google或者参见本文结尾的参考文章。
c).使用“锁定-编辑-解锁”模型
这个其实也是仁者见仁,智者见智的问题。只是对于我们而言,这个模型更加适合我们。但是SVN默认是“拷贝-修改-合并”模型,那如何进行变更呢?这就用到了SVN的自定义属性了。在SVN里是通过内置的属性“svn:needs-lock”实现“锁定-编辑-解锁”模型的。在这里只提一下具体操作,找到config文件,我的路径是:C:\Users\ljzforever\AppData\Roaming\Subversion\config。然后把enable-auto-props = yes前面的#号与空格删掉,然后在[auto-props]下面加一行* = svn:needs-lock=x。更加具体的操作与说明请自行google或者参见本文结尾的参考文章。这样,所有的文件在上传到SVN上后就自动进入“锁定-编辑-解锁”模型了。
d).关于"steal lock"
这个选项的初衷是为了增强灵活性,当一个文件被其它人锁定后,仍然可以用“偷”的方式将锁强行拿过来。不过这也是引起代码冲突的潜在原因之一,我们决定将其屏避掉。这里用到的技术就是hook script。hook script其实是一些脚本文件,在windows环境下就是一些bat脚本。每当svn做一些特定操作时,比如“锁定”,“提交”时,就会到指定目录下查找相关的hook script并执行它,达到对相应命令的预处理。我们这里是需要在锁定前检查用户在申请锁定文件时是否勾选了"steal lock",如果勾选了,则中断操作,提示用户这个选项已被系统禁止。放置这个hook script的目录是跟代码库路径是相关的,我的目录是:E:\Repositories\HWCH\hooks。安装后默认里面已经有若干个模板,以tmpl为扩展名,比如pre-lock.tmpl就是锁定前做的预操作,post-commit.tmpl就是提交后做的预操作。我们在里面新建一个文件:pre-lock.bat,然后在里面输入以下内容:
REM [1] REPOS-PATH (the path to this repository)
REM [2] PATH (the path in the repository about to be locked)
REM [3] USER (the user creating the lock)
REM [4] COMMENT (the comment of the lock)
REM [5] STEAL-LOCK (1 if the user is trying to steal the lock, else 0)
setlocal
::svn对代码资源库路径与文件路径里的右小括号敏感,需要对其转义
::代码资源库路径
set repos=%1
set "repos=%repos:)=^)%"
::当前文件路径
set repPath=%2
set "repPath=%repPath:)=^)%"
set userName=%3
set isSteal=%5
rem NO_STEALING
::如果没有被锁定,则直接跳走结束处理
if /I '1'=='%isSteal%' goto NO_STEALING
REM echo aaa >>d:\log.txt
REM echo repos = %repos% >>d:\log.txt
REM echo repPath = %repPath% >>d:\log.txt
REM echo userName = %userName% >>d:\log.txt
rem if the path has been locked, find the Owner.
::这里是处理重点
::通过svnlook lock %repos% %repPath%,命令获取锁信息,例如:
:: UUID Token: opaquelocktoken:1707b1a0-8dd1-a94e-87d2-6569a115cd5c
:: Owner: ljz
:: Created: 2011-08-08 21:05:31 +0800 (周一, 08 八月 2011)
:: Expires:
:: Comment (1 line):
::通过findstr /r /n ".",将所有行的前面加上行号,前返回所有行,例如:
:: 1:UUID Token: opaquelocktoken:1707b1a0-8dd1-a94e-87d2-6569a115cd5c
:: 2:Owner: ljz
:: 3:Created: 2011-08-08 21:05:31 +0800 (周一, 08 八月 2011)
:: 4:Expires:
:: 5:Comment (1 line):
::通过tokens=1,2,3 delims=: ,以:号与空格作为分隔符,将上述每一行分隔,并将前三段分别装入变量%%i,%%j,%%k
::通过if %%i == 2 set LockedName=%%k,把第二行分隔后的第三段装入变量LockedName,在这里,就是ljz
for /f "tokens=1,2,3 delims=: " %%i in ('svnlook lock %repos% %repPath% ^|findstr /r /n "."') do (
if %%i == 2 set LockedName=%%k
)
rem If we get no result from svnlook, there's no lock, allow the lock to happen.
::如果没有获取到锁定信息,则直接跳走结束处理
if not defined LockedName goto OK_EXIT
rem If the person locking matches the lock's owner, allow the lock to happen.
rem But this one won't effect, the SVN don't care if the person matchs, they just don't allow relock.
REM echo userName = %userName% >>d:\log.txt
REM echo LockedName = %LockedName% >>d:\log.txt
::如果锁定人与当前用户同名,则直接跳走结束处理
if /I '%LockedName%'=='%userName%' goto OK_EXIT
rem Otherwise, we've got an owner mismatch, so return failure:
:WRONG_PERSON
echo the path has been locked by %LockedName%, Pls contact %LockedName% to unlock it.>&2
goto ERROR_EXIT
:NO_STEALING
echo Stealing lock is not allowed at this server.>&2
:ERROR_EXIT
endlocal
exit 1
:OK_EXIT
endlocal
exit 0
这样,我们就能阻止用户使用"steal lock"选项了。
2.CruiseControl.NET
我下载的版本是CruiseControl.NET-1.6.7981.1,安装完成后,主要通过配置安装目录下的ccnet.config文件来达到自动化构建。
a).配置文件
我的配置文件如下:
<!-- This is your CruiseControl.NET Server Configuration file. Add your projects below! -->
<!--
<project name="MyFirstProject" />
-->
<project name="SVNTest" >
<!--公有参数-->
<!--CC.net存放工作结果的目录-->
<artifactDirectory>E:\project\SVNTest\artifactDir</artifactDirectory>
<!--需要进行持续集成的项目的目录-->
<workingDirectory >E:\project\SVNTest\workingDir</workingDirectory>
<!--编译报告的网址-->
<webURL>http://localhost/ccnet/server/local/ViewServerReport.aspx</webURL>
<!--编译的版本号与日志文件名格式 -->
<labeller type="dateLabeller"></labeller>
<!--该触发器控制周一到周五的8点到24点的时间段内进行编译-->
<!--在时间段内每900秒检查一次svn有无更新 ,如果有就取出并编译-->
<!--在时间段内每晚上11点50分进行一次强制编译-->
<!--但是目前有个问题,在周六与周日的23:59:59仍会编译一次,原因是我的时间段的结束时间写的是23:59:59。系统问题,无法解决-->
<triggers>
<multiTrigger operator="And">
<triggers>
<filterTrigger startTime="00:00:00" endTime="08:00:00">
<trigger type="filterTrigger" startTime="00:00:00" endTime="23:59:59">
<trigger type="intervalTrigger" seconds="900" />
<weekDays>
<weekDay>Saturday</weekDay>
<weekDay>Sunday</weekDay>
</weekDays>
</trigger>
</filterTrigger>
<filterTrigger startTime="00:00:00" endTime="08:00:00">
<trigger type="filterTrigger" startTime="00:00:00" endTime="23:59:59">
<trigger type="scheduleTrigger" time="23:50:00" buildCondition="ForceBuild" />
<weekDays>
<weekDay>Saturday</weekDay>
<weekDay>Sunday</weekDay>
</weekDays>
</trigger>
</filterTrigger>
</triggers>
</multiTrigger>
</triggers>
<!-- 版本控制系统的类型 -->
<sourcecontrol type="svn">
<!-- SVN的地址 -->
<trunkUrl>http://localhost:88/svn/HWCH/SVNTest/</trunkUrl>
<!-- 把源代码检出到该目录 -->
<workingDirectory>E:\project\SVNTest</workingDirectory>
<!-- svn.exe所在路径 -->
<executable>C:\Program Files\VisualSVN Server\bin\svn.exe</executable>
<!-- 登录svn的用户名和密码 -->
<username>ljz</username>
<password>ljz</password>
</sourcecontrol>
<tasks>
<!--采用VS进行编译-->
<devenv>
<solutionfile>E:\project\SVNTest\SVNTest.sln</solutionfile>
<configuration>Debug</configuration>
</devenv>
</tasks>
<publishers>
<!--编译日志存放路径-->
<!--<xmllogger logDir="E:\DailyBuild\Log\3\buildlogs" />-->
<!-- 下面设置编译报告通过email通知相关人员 详细信息参考cc.net的帮助-->
<!--<email from="admin@163.com" mailhost="smtp.163.com" mailhostUsername="admin" mailhostPassword="123" includeDetails="true">
<users>
<user name="dachuan" group="buildmaster" address="dachuan@163.com"/>
</users>
<groups>
<group name="buildmaster" notification="always"/>
</groups>
</email>-->
</publishers>
</project>
</cruisecontrol>
基本上照葫芦画瓢,就能把配置搞定。
b).服务器时间
我在测试cc.net从svn获取代码的时候,出现了一个这样子的错:failed:svn Check for modification。找了很久都没有找到原因,后来无意中改系统时间的时候才发现,原来我在测试triggers节点时,把系统时间改到昨天了,所以一定要记得,下一次获取代码的时间一定要晚于上一次获取代码的时间,不然cc.net就再也无法从svn获取最新的代码了。
c).组件
其实,在敏捷思想的自动化管理中,cc.net是一个持续构建的平台,它不仅有上述的功能,还可以整合其它的技术框架.这里有两张从其它地方转过来的图,可以略窥一二
1).NANT,MSBuild
虽说在上面的示例中,我们通过调用Visual Studio来完成项目的编译,但是实际上有其缺陷:一是如果当前环境没有安装Visual Studio,则无法完成编译,二是可供选择的编译参数较少.而NANT与MSBuild则是专门解决其编译问题的.MSBuild是微软提供的生成工具,被包含在.Net FrameWork环境之中,可以拖离Visual Studio对项目进行编译,可以进行参数化编译.NANT则是一个独立的第三方自动化编译框架,由JAVA领域的ANT转化而来,通过直接调用csc命令对项目进行编译,它不仅可以进行参数化编译,还可以做很多其它额外的事,比如将目标文件夹清空,copy额外的第三方dll,将编译好的项目打包压缩成zip文件等.如果说cc.net注意Build的管理,则NANT重注重于Build的运行.个人感觉可以通过Visual Studio或者MSBuild对项目进行编译,然后通过NANT做一些项目发布的其它工作.
2).FxCop
这是微软出的一个代码规范检查工具,也可以通过CC.Net对其进行自动化调用
3).NUnit,NCover
两个自动化测试框架,也可以通过CC.Net对其进行自动化调用
3.redmine
这是个著名的开源项目管理软件,这个就不多讲了,具体的请参看我之前的博文:项目管理软件之易度1.5,禅道2.0,redmine1.2(附redmine1.2的安装)
4.bugfree
这也是个著名的开源bug管理软件,下载最新的Wamp环境,我的是WampServer2.1e,解压安装,然后下载最新的bugfree,我的是bugfree2.1.3,解压后放在Wamp安装目录的www目录下,然后在浏览器地址栏里输入指定网址,按提示生成数据库,稍等几秒钟后就可以开始使用了。更详细的操作与说明请自行google或者参见本文结尾的参考文章。
最后,下载个花生壳, 将域名动态绑定下,全世界就都可以使用公司本部的代码了~~~
参考的文章: