数据库开发的持续集成 - CruiseControl.Net的项目配置
本系列文章目录
数据库开发的持续集成 - Sql Server 部署升级工具
数据库开发的持续集成 - Sql Server数据库结构比较
数据库开发的持续集成 - 方法和流程
数据库开发的持续集成 - Liquibase的简介和应用
数据库的持续集成 - CruiseControl.Net的项目配置
继续数据库的持续集成-方法和流程, 下面来说一下如何配置CruiseControl.Net的项目。
流程和配置详解
CC项目包括Trunk项目和Studio项目,由于Studio项目相对简单,下面只详解Trunk项目。
先回顾一下流程:
该流程直接操作基线数据库,需要使用脚本(ChangLog)升级基线数据库,然后在其上做测试等等工作。其中主要步骤详解如下:
* 首先用CCNetLabel给基线数据库打Tag(ci.proj中Tag属性缺省值为$(CCNetLabel)),为的是集成失败时将基线数据库回滚到集成之前的状态
* 之于其他工作前编译解决方案,是为了能够尽早发现编译错误
* 使用liquibase执行数据库部署脚本root.changelog.xml后,基线数据结构就能用于后续测试了
这样的流程,有几个好处:
* 由于基线数据和已发布出去的产品数据库结构相同,直接操作基线数据库可以保证即将发布的实体类、访问层和数据库升级脚本可用于平滑升级现有产品数据库和系统
* 集成失败的数据库回滚可保证失败的集成不在基线数据库中留下垃圾
* 在SVN上打Tag,可方便开发人员回溯系统发布历史上的任何一个时间点,可满足对产品系统打补丁的需求
关键点
1 数据库回滚 TagRollbackCC
该任务放在CC项目的publishers节中,保证无论集成成功还是失败都能执行到
其执行的条件是持续集成失败 Condition="$(CCNetIntegrationStatus) != 'Success'"
2 保证同一份测试代码在本地,CC上的Studio项目和Trunk项目都能应用
需要满足的条件包括:
* 测试代码在Studio项目中使用测试数据库GTCATest,在本地和Trunk中使用GTCA(本地数据库和基线数据同名,方便结构比较)
* 测试代码在Trunk项目中不能修改数据库结构,否则将破坏基线数据库的状态
解决的方案:
* 测试代码中通过预编译指令CC_Studio,CC_Trunk区分三种场景
* 要保证测试在不通场景使用不用的数据库,就不能把数据连接配置放在测试项目的app.config中,使用InPlaceConfigurationSource替代
CC项目配置
这个系列到此告一段落,回顾整个探索过程,曾经满世界的寻找数据库比较和同步的工具,自己也写过升级部署工具。直到liquibase进入视野,给了我足够的支撑,这才能够气定神闲的设计和丰满流程和方法,并将其转为实现。
衷心感谢liquibase的贡献者提供了如此出色的工具。
数据库开发的持续集成 - Sql Server 部署升级工具
数据库开发的持续集成 - Sql Server数据库结构比较
数据库开发的持续集成 - 方法和流程
数据库开发的持续集成 - Liquibase的简介和应用
数据库的持续集成 - CruiseControl.Net的项目配置
继续数据库的持续集成-方法和流程, 下面来说一下如何配置CruiseControl.Net的项目。
流程和配置详解
CC项目包括Trunk项目和Studio项目,由于Studio项目相对简单,下面只详解Trunk项目。
先回顾一下流程:
该流程直接操作基线数据库,需要使用脚本(ChangLog)升级基线数据库,然后在其上做测试等等工作。其中主要步骤详解如下:
* 首先用CCNetLabel给基线数据库打Tag(ci.proj中Tag属性缺省值为$(CCNetLabel)),为的是集成失败时将基线数据库回滚到集成之前的状态
* 之于其他工作前编译解决方案,是为了能够尽早发现编译错误
* 使用liquibase执行数据库部署脚本root.changelog.xml后,基线数据结构就能用于后续测试了
这样的流程,有几个好处:
* 由于基线数据和已发布出去的产品数据库结构相同,直接操作基线数据库可以保证即将发布的实体类、访问层和数据库升级脚本可用于平滑升级现有产品数据库和系统
* 集成失败的数据库回滚可保证失败的集成不在基线数据库中留下垃圾
* 在SVN上打Tag,可方便开发人员回溯系统发布历史上的任何一个时间点,可满足对产品系统打补丁的需求
关键点
1 数据库回滚 TagRollbackCC
该任务放在CC项目的publishers节中,保证无论集成成功还是失败都能执行到
其执行的条件是持续集成失败 Condition="$(CCNetIntegrationStatus) != 'Success'"
<!-- 按标记回滚(for CC.Net) -->
<Target Name="TagRollbackCC" Condition="$(CCNetIntegrationStatus) != 'Success'">
<Exec Command="$(LiquiBase) $(ConnectionParam) $(ChangeLogParam) rollback $(Tag) > $(ExecLog)" ContinueOnError="false"/>
<CallTarget Targets="CheckExec"/>
</Target>
<Target Name="TagRollbackCC" Condition="$(CCNetIntegrationStatus) != 'Success'">
<Exec Command="$(LiquiBase) $(ConnectionParam) $(ChangeLogParam) rollback $(Tag) > $(ExecLog)" ContinueOnError="false"/>
<CallTarget Targets="CheckExec"/>
</Target>
2 保证同一份测试代码在本地,CC上的Studio项目和Trunk项目都能应用
需要满足的条件包括:
* 测试代码在Studio项目中使用测试数据库GTCATest,在本地和Trunk中使用GTCA(本地数据库和基线数据同名,方便结构比较)
* 测试代码在Trunk项目中不能修改数据库结构,否则将破坏基线数据库的状态
解决的方案:
* 测试代码中通过预编译指令CC_Studio,CC_Trunk区分三种场景
* 要保证测试在不通场景使用不用的数据库,就不能把数据连接配置放在测试项目的app.config中,使用InPlaceConfigurationSource替代
Dictionary<string, string> properties = new Dictionary<string, string>();
properties.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver");
properties.Add("dialect", "NHibernate.Dialect.MsSql2000Dialect");
properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
#if CC_STUDIO
/* CC 上的Studio编译,对GTCATest数据进行测试 */
properties.Add("hibernate.connection.connection_string", "Data Source=.;Initial Catalog=GTCATest;Integrated Security=SSPI");
#else
/* 本地编译和CC 上的Trunk编译,对GTCA数据进行测试 */
properties.Add("connection.connection_string", "Data Source=.;Initial Catalog=GTCA;Integrated Security=SSPI");
#endif
InPlaceConfigurationSource source = new InPlaceConfigurationSource();
source.Add(typeof (ActiveRecordBase), properties);
ActiveRecordStarter.Initialize(Assembly.Load("GTCA.Entities"), source);
* 对于CreateSchem/UpdateSchema/DropSchema等函数提供统一的工具类给开发人员使用properties.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver");
properties.Add("dialect", "NHibernate.Dialect.MsSql2000Dialect");
properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
#if CC_STUDIO
/* CC 上的Studio编译,对GTCATest数据进行测试 */
properties.Add("hibernate.connection.connection_string", "Data Source=.;Initial Catalog=GTCATest;Integrated Security=SSPI");
#else
/* 本地编译和CC 上的Trunk编译,对GTCA数据进行测试 */
properties.Add("connection.connection_string", "Data Source=.;Initial Catalog=GTCA;Integrated Security=SSPI");
#endif
InPlaceConfigurationSource source = new InPlaceConfigurationSource();
source.Add(typeof (ActiveRecordBase), properties);
ActiveRecordStarter.Initialize(Assembly.Load("GTCA.Entities"), source);
public static void PrepareSchema()
{
#if !CC_TRUNK
/* 只有非CC上的Trunk编译,才允许对数据库结构发生变化*/
ActiveRecordStarter.UpdateSchema();
#endif
}
public static void DropSchema()
{
#if !CC_TRUNK
/* 只有非CC上的Trunk编译,才允许对数据库结构发生变化*/
ActiveRecordStarter.DropSchema();
#endif
}
{
#if !CC_TRUNK
/* 只有非CC上的Trunk编译,才允许对数据库结构发生变化*/
ActiveRecordStarter.UpdateSchema();
#endif
}
public static void DropSchema()
{
#if !CC_TRUNK
/* 只有非CC上的Trunk编译,才允许对数据库结构发生变化*/
ActiveRecordStarter.DropSchema();
#endif
}
CC项目配置
<project name="Database-trunk" webURL="http://192.168.1.44/ccnet/server/local/project/Database-trunk/ViewProjectReport.aspx" >
<workingDirectory>D:\GTCA\Database\trunk</workingDirectory>
<artifactDirectory>D:\GTCA\Database\trunk</artifactDirectory>
<category>GTCA</category>
<labeller type="taoLabeller">
<majorVersion>1</majorVersion>
<minorVersion>0</minorVersion>
</labeller>
<triggers/>
<sourcecontrol type="filtered">
<sourceControlProvider type="svn" autoGetSource="true" tagOnSuccess="true">
<trunkUrl>svn://192.168.1.4/Database/trunk</trunkUrl>
<workingDirectory>D:\GTCA\Database\trunk</workingDirectory>
<tagBaseUrl>svn://192.168.1.4/Database/tags</tagBaseUrl>
</sourceControlProvider>
<inclusionFilters>
<userFilter>
<names><name>zt</name></names>
</userFilter>
</inclusionFilters>
<exclusionFilters>
<pathFilter><pattern>/**/test/**</pattern></pathFilter>
<pathFilter><pattern>/**/document/**</pattern></pathFilter>
</exclusionFilters>
</sourcecontrol>
<prebuild>
<!--tag GTCA with CCNetLabel-->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:tag </buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</prebuild>
<tasks>
<!-- build source -->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk</workingDirectory>
<projectFile>Database.sln</projectFile>
<buildArgs>/noconsolelogger /t:rebuild /p:configuration=release /p:DefineConstants=CC_TRUNK</buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
<!-- deploy GTCA -->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:deploy </buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
<!-- test entities and DAL on GTCA -->
<nunit>
<path>D:\GTCA\reference\tools\nunit\nunit-console.exe</path>
<assemblies>
<assembly>D:\GTCA\Database\trunk\test\TestEntities\bin\Release\TestEntities.dll</assembly>
</assemblies>
</nunit>
<nunit>
<path>D:\GTCA\reference\tools\nunit\nunit-console.exe</path>
<assemblies>
<assembly>D:\GTCA\Database\trunk\test\TestDal\bin\Release\TestDal.dll</assembly>
</assemblies>
</nunit>
<!-- generate database document on GTCA-->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:doc /p:DocDir=D:\dbdoc</buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
<!--publish GTCA -->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:publish</buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</tasks>
<publishers>
<xmllogger />
<modificationHistory onlyLogWhenChangesFound="true" />
<!-- tagrollback on error( if CCNetIntegrationStatus isn't Success) -->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:TagRollbackCC</buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</publishers>
<externalLinks>
<externalLink name="Database Document" url="http://192.168.1.44/dbdoc/" />
</externalLinks>
</project>
<workingDirectory>D:\GTCA\Database\trunk</workingDirectory>
<artifactDirectory>D:\GTCA\Database\trunk</artifactDirectory>
<category>GTCA</category>
<labeller type="taoLabeller">
<majorVersion>1</majorVersion>
<minorVersion>0</minorVersion>
</labeller>
<triggers/>
<sourcecontrol type="filtered">
<sourceControlProvider type="svn" autoGetSource="true" tagOnSuccess="true">
<trunkUrl>svn://192.168.1.4/Database/trunk</trunkUrl>
<workingDirectory>D:\GTCA\Database\trunk</workingDirectory>
<tagBaseUrl>svn://192.168.1.4/Database/tags</tagBaseUrl>
</sourceControlProvider>
<inclusionFilters>
<userFilter>
<names><name>zt</name></names>
</userFilter>
</inclusionFilters>
<exclusionFilters>
<pathFilter><pattern>/**/test/**</pattern></pathFilter>
<pathFilter><pattern>/**/document/**</pattern></pathFilter>
</exclusionFilters>
</sourcecontrol>
<prebuild>
<!--tag GTCA with CCNetLabel-->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:tag </buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</prebuild>
<tasks>
<!-- build source -->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk</workingDirectory>
<projectFile>Database.sln</projectFile>
<buildArgs>/noconsolelogger /t:rebuild /p:configuration=release /p:DefineConstants=CC_TRUNK</buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
<!-- deploy GTCA -->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:deploy </buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
<!-- test entities and DAL on GTCA -->
<nunit>
<path>D:\GTCA\reference\tools\nunit\nunit-console.exe</path>
<assemblies>
<assembly>D:\GTCA\Database\trunk\test\TestEntities\bin\Release\TestEntities.dll</assembly>
</assemblies>
</nunit>
<nunit>
<path>D:\GTCA\reference\tools\nunit\nunit-console.exe</path>
<assemblies>
<assembly>D:\GTCA\Database\trunk\test\TestDal\bin\Release\TestDal.dll</assembly>
</assemblies>
</nunit>
<!-- generate database document on GTCA-->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:doc /p:DocDir=D:\dbdoc</buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
<!--publish GTCA -->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:publish</buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</tasks>
<publishers>
<xmllogger />
<modificationHistory onlyLogWhenChangesFound="true" />
<!-- tagrollback on error( if CCNetIntegrationStatus isn't Success) -->
<msbuild>
<workingDirectory>D:\GTCA\Database\trunk\deploy</workingDirectory>
<projectFile>ci.proj</projectFile>
<buildArgs>/noconsolelogger /t:TagRollbackCC</buildArgs>
<logger>D:\GTCA\reference\libs\MSBuild\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</publishers>
<externalLinks>
<externalLink name="Database Document" url="http://192.168.1.44/dbdoc/" />
</externalLinks>
</project>
这个系列到此告一段落,回顾整个探索过程,曾经满世界的寻找数据库比较和同步的工具,自己也写过升级部署工具。直到liquibase进入视野,给了我足够的支撑,这才能够气定神闲的设计和丰满流程和方法,并将其转为实现。
衷心感谢liquibase的贡献者提供了如此出色的工具。