Jenkins+MSBuild+Svn搭建持续集成环境Part 2: 创建NopCommerce的MSBuild构建脚本

上一节 Jenkins+MSBuild+Svn搭建持续集成环境Part 1: MSBuild 对MSBuild做了简单介绍,那么这一节就针对实际的项目来创建MSBuild构建脚本。对于简单的小项目,也许直接通过 msbuild xxxx.csproj就能得到你想要的结果,但是对于复杂的项目,那就需要根据你想要的构建结果去创建相应的构建脚本。

我们公司的站点用的是一个叫NopCommerce的框架,NopCommerce是国外的一个高质量的开源b2c网站系统,具有很强的插件机制,功能很强大,很适合用来做外贸系统和二次开发。

整个项目的结构大体是这样的:

picgo_github_figurebed20200114150343.png

红框里面的,就是整个项目的核心部分。

之前我每次发布系统都是直接在Visual Studio里面对Nop.Web这个项目右键点发布,发布之后结构是这样的:

14933357-c499b0a3b662bc2c.png

然后我一般会删掉一些不必要的文件夹和文件,比如App_Data整个文件夹删掉,然后Plugins一般没改动我也会删掉,最后,压缩上传服务器替换发布。这一整个流程下来,要耗费我很多时间,特别需要频繁发布测试的时候,一天下来其它活儿都不用干了,就等发布就行了。所以,才会考虑用Jenkins和MSBuild做自动化发布。然后我尝试了直接使用msbuild命令对NopCommerce.sln文件和Nop.Web.csproj文件直接进行构建,但是都得不到我想要的结果,要不就是少了文件不然就是文件夹结构不对。在网上也基本找不到使用MSBuild构建NopCommerce的例子。根据NopCommerce官方团队成员的说法,构建必须得按照以下要求:

  1. Nop.Web and Nop.Admin are two web applications. They both need to be published to the same directory. “Nop.Web” to “Published\Web\”. “Nop.Admin” to “Published\Web\Administration\”. Make sure the .dlls from Nop.Admin (Published\Web\Administration\bin) are moved and exist in (Published\Web\bin).
  2. Ensure that plugins (\Presentation\Nop.Web\Plugins) are copied into \Published\Web\Plugins\ directory
  3. Select all the files in Published\Web\ directory and upload them to your web server.

所以,我决定一步步的去写构建脚本,然后得到我想要的结果。

Step 1 | 创建一个MSBuild XML文件

首先创建一个文件,命名为NopCommerce.msbuild,这个扩展名只是方便我们认出它是一个MSBuild脚本,也可以不写扩展名,然后往这个文件里面添加一个Project作为根元素:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
</Project>

Step 2 | 添加一组Properties:

Project里面添加一组PropertyGroup

<PropertyGroup>
  <RootFolder>$(MSBuildProjectDirectory)</RootFolder>
  <DeployBranch  Condition="'$(DeployBranch)' == ''">qa</DeployBranch>
  <DeployFolder  Condition="'$(DeployFolder)' == ''">$(RootFolder)\Release</DeployFolder>
  <WebFolder>$(RootFolder)\$(DeployBranch)\Presentation\Nop.Web</WebFolder>
  <Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
  <Platform Condition="'$(Platform)' == ''">Any CPU</Platform>
</PropertyGroup>

RootFolder是根目录,DeployBranch发布分支,DeployFolder输出文件夹,默认为根目录底下的Release文件夹,WebFolderNop.Web的文件夹,Configuration默认为Release版本,Platform默认Any CPU

Step 3 | 添加一个Target

这个Target命名为Deploy,它将包含我们接下来要做的所有Task,包括创建文件夹,生成项目,删除不必要的文件,移动文件等:

<Target Name="Deploy">
</Target>

Step 4 | 删除并创建发布文件夹

首先,将之前发布过的历史文件夹先删除再创建,确保输出目录是干净的

<!--删除发布文件夹-->
<RemoveDir Directories="$(DeployFolder)" />
<!-- 创建发布文件夹 -->
<MakeDir Directories="$(DeployFolder)" />

Step 5 | 添加生成主程序的Task

生成Nop.WebNop.Admin,这两个项目分别是网站的前台和后台,然后配置web文件的输出目录,和bin目录文件夹,Configuration根据传入的配置决定,可以是Release或者是Debug版本:

<!--开始发布 Nop.Web-->
<MSBuild Projects="$(RootFolder)\$(DeployBranch)\Presentation\Nop.Web\Nop.Web.csproj"
   Targets="ResolveReferences;_CopyWebApplication"
   Properties="WebProjectOutputDir=$(DeployFolder)\;
       OutDir=$(DeployFolder)\bin\;Configuration=$(Configuration)" />


<!--开始发布 Nop.Admin-->
<MSBuild Projects="$(RootFolder)\$(DeployBranch)\Presentation\Nop.Web\Administration\Nop.Admin.csproj" Targets="ResolveReferences;_CopyWebApplication"
   Properties="WebProjectOutputDir=$(DeployFolder)\Administration\;
   OutDir=$(DeployFolder)\Administration\bin\;Configuration=$(Configuration)" />

Step 6 | 删除不必要的文件与文件夹

一般来说有很多config文件在发布的时候是不会覆盖的,比如测试环境和生产环境的配置一般不同,如果覆盖了就会出现问题,还有其实Administration文件夹里面只需要Contents跟Views,其他都是不需要的,所以我们把它删掉:

<!--删除Nop.admin里面不必要的文件夹-->
<RemoveDir Directories="$(DeployFolder)\Administration\bin\;
$(DeployFolder)\Administration\App_Data\;
$(DeployFolder)\Administration\Collection\;
$(DeployFolder)\Administration\Scripts\;
$(DeployFolder)\Administration\Themes\;
$(DeployFolder)\App_Data\;" />

<!--删除不必要的文件-->
<Delete Files="$(DeployFolder)\Administration\packages.config;
             $(DeployFolder)\packages.config;
             $(DeployFolder)\Web.config;
             $(DeployFolder)\favicon.ico;
             $(DeployFolder)\FileNotFound.html;
             $(DeployFolder)\Global.asax"/>

Step 7 | 复制需要的资源文件

<RemoveDir Directories="$(DeployFolder)\Content\Images\uploaded\" />
<CreateItem Include="$(RootFolder)\$(DeployBranch)\Presentation\Nop.Web\Content\Images\uploaded\**\*.*">
  <Output TaskParameter="Include" ItemName="uploadedImages" />
</CreateItem>
<Copy SourceFiles="@(uploadedImages)"
        DestinationFolder="$(DeployFolder)\Content\Images\uploaded\%(RecursiveDir)"
        SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" />

Step 8 | 创建 Plugins\Payments.OceanPayment文件夹

我们的站点用到一个叫OceanPayment的插件,这个插件引用到了项目的一些其他类库,生成之后在这个插件的bin目录底下需要包含一些它引用到的项目内其它类库的dll,比如Nop.Web.dll,Nop.Admin.dll,因为dll的加载顺序问题,如果不替换这些dll可能会导致程序发布后,程序用的这些dll仍是旧版本的。曾经在一次发布后发现增加的功能没生效,困扰了我一个下午,后来才发现是因为没替换这个插件里面的这些dll。当然这个步骤不是必要的,如果你的插件没引用到项目其他类库,那就不需要,而且我这边没有去生成其他插件,因为一般不会去修改到插件的代码,假如有动到插件那么再手动发布就好了。

<!-- 创建 Plugins\Payments.OceanPayment文件夹-->
<MakeDir Directories="$(DeployFolder)\Plugins\Payments.OceanPayment" />
<!-- 将根目录bin文件夹里面Nop开头的文件复制一份到Plugins\Payments.OceanPayment -->
<CreateItem Include="$(DeployFolder)\bin\Nop.*">
  <Output TaskParameter="Include" ItemName="CompileOutputPlugin" />
</CreateItem>
<Copy SourceFiles="@(CompileOutputPlugin)"
        DestinationFolder="$(DeployFolder)\Plugins\Payments.OceanPayment\" /> 

最后我们在Project元素里面将其DefaultTargets设置为Deploy这个Target

<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  ...
</Project>

至此,我们的构建脚本已经基本完成,我们来看一下完整的脚本内容:

<?xml version="1.0" encoding="utf-8" ?>
<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <RootFolder>$(MSBuildProjectDirectory)</RootFolder>
    <DeployBranch  Condition="'$(DeployBranch)' == ''">qa</DeployBranch>
    <DeployFolder  Condition="'$(DeployFolder)' == ''">$(RootFolder)\Release</DeployFolder>
    <WebFolder>$(RootFolder)\$(DeployBranch)\Presentation\Nop.Web</WebFolder>
    <Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
    <Platform Condition="'$(Platform)' == ''">Any CPU</Platform>
  </PropertyGroup>

  <Target Name="Deploy">
    <!--删除发布文件夹-->
    <RemoveDir Directories="$(DeployFolder)" />
    <!-- 创建发布文件夹 -->
    <MakeDir Directories="$(DeployFolder)" />

    <!--开始发布 Nop.Web-->
    <MSBuild Projects="$(RootFolder)\$(DeployBranch)\Presentation\Nop.Web\Nop.Web.csproj"
       Targets="ResolveReferences;_CopyWebApplication"
       Properties="WebProjectOutputDir=$(DeployFolder)\;
           OutDir=$(DeployFolder)\bin\;Configuration=$(Configuration)" />


    <!--开始发布 Nop.Admin-->
    <MSBuild Projects="$(RootFolder)\$(DeployBranch)\Presentation\Nop.Web\Administration\Nop.Admin.csproj"
       Targets="ResolveReferences;_CopyWebApplication"
       Properties="WebProjectOutputDir=$(DeployFolder)\Administration\;
           OutDir=$(DeployFolder)\Administration\bin\;Configuration=$(Configuration)" />


    <!--将 Nop.Admin/bin/ 的文件移动到根目录 bin/-->
    <CreateItem Include="$(DeployFolder)\Administration\bin\*.dll">
      <Output TaskParameter="Include" ItemName="CompileOutput" />
    </CreateItem>
    <Copy SourceFiles="@(CompileOutput)"
            DestinationFolder="$(DeployFolder)\bin\" />


    <!--删除Nop.admin里面不必要的文件夹-->
    <RemoveDir Directories="$(DeployFolder)\Administration\bin\;
    $(DeployFolder)\Administration\App_Data\;
    $(DeployFolder)\Administration\Collection\;
    $(DeployFolder)\Administration\Scripts\;
    $(DeployFolder)\Administration\Themes\;
    $(DeployFolder)\App_Data\;" />


    <!--删除不必要的文件-->
    <Delete Files="$(DeployFolder)\Administration\packages.config;
                 $(DeployFolder)\packages.config;
                 $(DeployFolder)\Web.config;
                 $(DeployFolder)\favicon.ico;
                 $(DeployFolder)\FileNotFound.html;
                 $(DeployFolder)\Global.asax"/>

    <!--复制 \Contents\Images\uploaded\ directory-->
    <RemoveDir Directories="$(DeployFolder)\Content\Images\uploaded\" />
    <CreateItem Include="$(RootFolder)\$(DeployBranch)\Presentation\Nop.Web\Content\Images\uploaded\**\*.*">
      <Output TaskParameter="Include" ItemName="uploadedImages" />
    </CreateItem>
    <Copy SourceFiles="@(uploadedImages)"
            DestinationFolder="$(DeployFolder)\Content\Images\uploaded\%(RecursiveDir)"
            SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" />

     <!-- 创建 Plugins\Payments.OceanPayment文件夹-->
    <MakeDir Directories="$(DeployFolder)\Plugins\Payments.OceanPayment" />
    <!-- 将根目录bin文件夹里面Nop开头的文件复制一份到Plugins\Payments.OceanPayment -->
    <CreateItem Include="$(DeployFolder)\bin\Nop.*">
      <Output TaskParameter="Include" ItemName="CompileOutputPlugin" />
    </CreateItem>
    <Copy SourceFiles="@(CompileOutputPlugin)"
            DestinationFolder="$(DeployFolder)\Plugins\Payments.OceanPayment\" /> 

    <!-- 至此构造完成,整个文件夹直接替换到服务器对应的目录 -->
  </Target>
</Project>

执行这个构建脚本,最终生成的文件夹结构,然后就可以将这些文件全部替换到服务器的程序目录下了

14933357-0fdcb546ccb47539.png

下一节,Jenkins的配置。

参考:

ASP.NET MVC 使用MSBuild生成的几个注意事项

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容