【Hello CC.NET】自動化發布時 Web.config 文件維護


《【Hello CC.NET】CC.NET 實現自動化集成》 的 HellowWorld 中經實現:

1.獲取源碼

2.編譯項目

3.集成測試

4.Ftp發布項目

5.創建安裝包

6.郵件通知

在方案落地的過程中,FTP 上傳開發自測環境(或測試環境)后,仍然需要人手來修改相關配置(比如 Web.config)。

假設項目的 Web.config 配置如下:

  <appSettings>
    <add key="OrgDB" value="xxx_dev_db"/>
    <add key="BusinessDB" value="xxx_DEV_DB"/>
    <add key="RedisCache" value="192.168.10.111:6379"/>
    <add key="SSOService" value="http://192.168.10.111/DEV/V1.1.9/SSOService.svc"/>
    <add key="Coder" value="蔡海華"/>
  </appSettings> 

 在發布到測試環境之前,我們需要把 _dev_db 替換為 _test_db,192.168.10.111:6379 替換為 192.168.10.112:6379 ,http://192.168.10.111/DEV/V1.1.9/SSOService.svc 替換為 http://192.168.10.111/Test/V1.1.9/SSOService.svc(忽略大小寫)。

 

一、用 bat 腳本實現文件拷貝和web.config內容替換

1.文件拷貝

為了不影響舊的環境,我們新建一個文件夾 PackageDirectory 來保存修改后的文件。

文件拷貝用批處理來實現:新建一個 txt 文件,輸入以下內容,另存為 DFCopier.bat(directories & files copier),copyfrom 和 copyto 由參數方式傳入。

@echo off  
set from=%1
set to=%2

::rd /q/s "%to%"&&md "%to%"
dir /a /b %to%|findstr .>nul 2>nul && goto havefiles || goto nofiles  
      
:havefiles  
rem echo 有文件  
goto end 
      
:nofiles  
rem echo 沒有文件  
XCOPY %from% %to% /c/q/e/r
:end

 

2.文件內容替換

新建一個 txt 文件,輸入以下內容,另存為 FCReplacer.bat(file content replacer),directory , file, from, to 由參數方式傳入。

@echo off
set dir=%1
set file=%2
set from=%3
set to=%4

setlocal enabledelayedexpansion
pushd %dir%
for /f "tokens=1,2* delims=:" %%i in ('findstr /n ".*" %file%') do (
    set txt=%%j
    if "!txt!" == "" (
        echo.>>%dir%%file%.tmp
    ) else (
        echo !txt:%from%=%to%!>>%dir%%file%.tmp
    )
)
move /y %dir%%file%.tmp %file%
:end

3.配置 ccnet.config

在 tasks 節點中添加以下兩個子節點。假設一個最簡單的場景:替換數據庫命名 xxx_dev_db 為 xxx_test_db。

      <!-- 復制文件到 Package 目錄 -->
      <exec>
        <executable>C:\CC.NET\Server\DFCopier.bat</executable>
        <buildArgs>C:\CC.NET\Server\Test\PublishDirectory C:\CC.NET\Server\Test\PackageDirectory</buildArgs>
      </exec>
      <!-- 替換 Package 目錄下的 WebConfig 文件 -->
      <exec>
        <executable>C:\CC.NET\Server\FCReplacer.bat</executable>
        <buildArgs>C:\CC.NET\Server\Test\PackageDirectory\WcfService Web.config _dev_db _test_db</buildArgs>
      </exec>

ftp 和 package 部分修改如下:

      <!--ftp發布Wcf服務到開發環境-->
      <ftp>
        <serverName>127.0.0.1</serverName>
        <userName>admin</userName>
        <password>admin</password>
        <action>UploadFolder</action>
        <ftpFolderName></ftpFolderName>
        <localFolderName>C:\CC.NET\Server\Test\PackageDirectory\WcfService</localFolderName>
        <recursiveCopy>true</recursiveCopy>
        <timeDifference>1</timeDifference>
      </ftp>
      <package>
        <name>Lib.sln</name>
        <compression>9</compression>
        <manifest type="defaultManifestGenerator" />
        <packageList>
          <packageFile sourceFile="C:\CC.NET\Server\Test\PackageDirectory\WcfService\*.svc" targetFolder="WcfService" />
          <packageFile sourceFile="C:\CC.NET\Server\Test\PackageDirectory\WcfService\*.Release.config" targetFolder="WcfService" />
          <packageFile sourceFile="C:\CC.NET\Server\Test\PackageDirectory\WcfService\bin\*.dll" targetFolder="WcfService\bin" />
          <!--<packageFolder sourceFolder="C:\CC.NET\Server\Test\PublishDirectory\WcfService" targetFolder="WcfService" 
                         fileFilter="*.*" flatten="false" includeSubFolders="false" />-->
        </packageList>
      </package>

 

4.測試

Forcebuild 得到以下結果(只截取了 DFCopier.bat 和 FCReplacer.bar 相關的部分):

打開 Web.config 文件卻發現中文會變成亂碼。

二、自己實現文件內容替換功能

批處理的方式在中文面前顯得有點蒼白無力,另外一些更復雜的場景是無法簡單地 replace 來解決。一番 Google 折騰后我決定自己寫一個 replacer,支持正則來處理可能出現的復雜需求。

1.實現 replacer

新建一個項目,命名為 FCReplacer。實現的代碼 100 行不到。編譯得到 FCReplacer.exe

    class Program
    {
        static void Main(string[] args)
        {
            var file = GetFileName(args);
            var replacers = GetReplaers(args);

            if (file == null || replacers == null || replacers.Count() == 0)
                return;

            var txt = File.ReadAllText(file);

            Console.WriteLine(string.Format("Replace content of file({0})", file));
            foreach (var replacer in replacers)
            {
                Console.WriteLine(string.Format("Replace {0} to {1}", replacer.From, replacer.To));
                txt = replacer.Replace(txt);
            }
            File.WriteAllText(file, txt);
        }
        static string GetFileName(string[] args)
        {
            string filename = null;
            var fileRegex = new Regex("^/file=(=?.*)$", RegexOptions.Compiled);
            foreach (var arg in args)
            {
                var match = fileRegex.Match(arg); 
                if (match.Groups.Count == 2)
                {
                    filename = match.Groups[1].Value.Trim();
                    break;
                }
            }
            return filename;
        }
        static IEnumerable<Replacer> GetReplaers(string[] args)
        {
            var list = new List<Replacer>();
            var replacerRegex = new Regex("^/from=(=?.*)/to=(=?.*)$", RegexOptions.Compiled);
            foreach (var arg in args)
            {
                var match = replacerRegex.Match(arg);
                if (match.Groups.Count == 3)
                {
                    var from = match.Groups[1].Value.Trim();
                    var to = match.Groups[2].Value.Trim();
                    list.Add(new Replacer() { From = from, To = to });
                }
            }
            return list;
        }
        class Replacer
        {
            public string From { get; set; }
            public string To { get; set; }
            public string Replace(string txt, RegexOptions regexOption = RegexOptions.IgnoreCase)
            {
                txt = Regex.Replace(txt, this.From, this.To, regexOption);
                return txt;
            }
        }
    }

 

 

2.配置 ccnet.config

把文件替換的批處理部分改為 FCReplacer.exe。替換的邏輯通過參數方式傳入。假設三個更復雜的場景:

(1) 替換數據庫名 xxx_dev_db 為 xxx_test_db

(2)替換 redis 緩存服務的地址 192.168.10.111:PORT 為 192.168.10.112:PORT,端口設置不變

(3)替換 SSO 服務地址,版本號不變

      <!-- 復制文件到 Package 目錄 -->
      <exec>
        <executable>C:\CC.NET\Server\DFCopier.bat</executable>
        <buildArgs>C:\CC.NET\Server\Test\PublishDirectory C:\CC.NET\Server\Test\PackageDirectory</buildArgs>
      </exec>
      <!-- 替換 Package 目錄下的 WebConfig 文件 -->
      <!--<exec>
        <executable>C:\CC.NET\Server\FCReplacer.bat</executable>
        <buildArgs>C:\CC.NET\Server\Test\PackageDirectory\WcfService Web.config _dev_db _test_db</buildArgs>
      </exec>-->
      <exec>
        <executable>C:\CC.NET\Server\FCReplacer.exe</executable>
        <buildArgs> /file=C:\CC.NET\Server\Test\PackageDirectory\WcfService\Web.config
 /from=_dev_db/to=_test_db
 /from=http://192.168.10.111/Dev/V(=?\d+(.\d+)*)/SSOService.svc/to=http://192.168.10.111/Test/V$1/SSOService.svc
        </buildArgs>
      </exec>

 

3.測試

Forcebuild 得到以下結果(只截取了 DFCopier.bat 和 FCReplacer.exe 相關的部分):

打開 Web.config 文件,三個替換邏輯都已經實現,中文也不再是亂碼。

 

完整的 ccnet.config 配置如下:

<cruisecontrol xmlns:cb="urn:ccnet.config.builder">
  <project name="Lib.Sln">
    <!--標簽-->
    <labeller type="dateLabeller"/>
    <artifactDirectory>C:\CC.NET\Server\Test\ArtifactDirectory</artifactDirectory>
    <!--項目的目錄-->
    <workingDirectory >C:\CC.NET\Server\Test\WorkingDirectory</workingDirectory>
    <!--自動構建結果的查看地址-->
    <webURL>http://vw-caihaihua/CC/server/local/project/Lib.Sln/ViewProjectReport.aspx</webURL>
    <!--自動運行時間間隔-->
    <triggers>
      <!--源碼修改觸發-->
      <intervalTrigger seconds="10" buildCondition="IfModificationExists " />
      <!--每日構建-->
      <scheduleTrigger time="19:00" buildCondition="ForceBuild">
        <weekDays>
          <!--<weekDay>Sunday</weekDay>-->
          <weekDay>Monday</weekDay>
          <weekDay>Tuesday</weekDay>
          <weekDay>Wednesday</weekDay>
          <weekDay>Thursday</weekDay>
          <weekDay>Friday</weekDay>
          <!--<weekDay>Saturday</weekDay>-->
        </weekDays>
      </scheduleTrigger>
    </triggers>
    <!--對源碼修改延遲處理時間間隔-->
    <modificationDelaySeconds>30</modificationDelaySeconds>
    <maxSourceControlRetries>5</maxSourceControlRetries>
    <!--源代碼管理(SVN)-->
    <sourcecontrol type="svn">
      <trunkUrl>https://vw-caihaihua/svn/Test/trunk/</trunkUrl>
      <executable>C:\Program Files (x86)\VisualSVN Server\bin\svn.exe</executable>
      <workingDirectory>C:\CC.NET\Server\Test\WorkingDirectory\</workingDirectory>
      <username>ci</username>
      <password>123456</password>
    </sourcecontrol>
    <tasks>
      <!--<devenv>
        <solutionfile>C:\CC.NET\Server\Test\WorkingDirectory\Lib.sln</solutionfile>
        <configuration>Debug</configuration>
      </devenv>-->
      <!--清理解決方案-->
      <msbuild>
        <executable>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</executable>
        <buildArgs>/t:clean /t:rebuild /p:configuration=debug</buildArgs>
        <workingDirectory>C:\CC.NET\Server\Test\WorkingDirectory</workingDirectory>
        <projectFile>Lib.sln</projectFile>
        <logger>ThoughtWorks.CruiseControl.MsBuild.XmlLogger,C:\Program Files (x86)\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
      </msbuild>
      <!--運行UnitTest-->
      <exec>
        <executable>C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe</executable>
        <baseDirectory>C:\CC.NET\Server\Test\WorkingDirectory</baseDirectory>
        <buildArgs>/testcontainer:LibTest\bin\Debug\LibTest.dll</buildArgs>
        <buildTimeoutSeconds>6000</buildTimeoutSeconds>
      </exec>
      <!--發布Wcf服務到本機-->
      <msbuild>
        <executable>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</executable>
        <workingDirectory>C:\CC.NET\Server\Test\WorkingDirectory\WcfService</workingDirectory>
        <projectFile>WcfService.csproj</projectFile>
        <buildArgs>
          /t:ResolveReferences;Compile
          /t:_CopyWebApplication
          /p:Configuration=Release
          /p:WebProjectOutputDir=C:\CC.NET\Server\Test\PublishDirectory\WcfService
          /p:OutputPath=C:\CC.NET\Server\Test\PublishDirectory\WcfService\bin
        </buildArgs>
      </msbuild>
      <!--運行UnitTest-->
      <exec>
        <executable>C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe</executable>
        <baseDirectory>C:\CC.NET\Server\Test\WorkingDirectory</baseDirectory>
        <buildArgs>/testcontainer:WcfServiceTest\bin\Debug\WcfServiceTest.dll</buildArgs>
        <buildTimeoutSeconds>6000</buildTimeoutSeconds>
      </exec>
      <!--啟動 Asp.NET Development Server-->
      <!--<exec>
        <executable>C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40.EXE</executable>
        <buildArgs>/port:9999 /path:C:\CC.NET\Server\Test\PublishDirectory\WcfService</buildArgs>
        <buildTimeoutSeconds>6000</buildTimeoutSeconds>
      </exec>-->

   
<!--ADDED START-->
<!-- 復制文件到 Package 目錄 --> <exec> <executable>C:\CC.NET\Server\DFCopier.bat</executable> <buildArgs>C:\CC.NET\Server\Test\PublishDirectory C:\CC.NET\Server\Test\PackageDirectory</buildArgs> </exec> <!-- 替換 Package 目錄下的 WebConfig 文件 --> <!--<exec> <executable>C:\CC.NET\Server\FCReplacer.bat</executable> <buildArgs>C:\CC.NET\Server\Test\PackageDirectory\WcfService Web.config _dev_db _test_db</buildArgs> </exec>--> <exec> <executable>C:\CC.NET\Server\FCReplacer.exe</executable> <buildArgs> /file=C:\CC.NET\Server\Test\PackageDirectory\WcfService\Web.config /from=_dev_db/to=_test_db /from=http://192.168.10.111/Dev/V(=?\d+(.\d+)*)/SSOService.svc/to=http://192.168.10.111/Test/V$1/SSOService.svc a </buildArgs> </exec>
   
<!--ADDED END-->

<!--ftp發布Wcf服務到開發環境--> <ftp> <serverName>127.0.0.1</serverName> <userName>admin</userName> <password>admin</password> <action>UploadFolder</action> <ftpFolderName></ftpFolderName> <localFolderName>C:\CC.NET\Server\Test\PackageDirectory\WcfService</localFolderName> <recursiveCopy>true</recursiveCopy> <timeDifference>1</timeDifference> </ftp> <package> <name>Lib.sln</name> <compression>9</compression> <manifest type="defaultManifestGenerator" /> <packageList> <packageFile sourceFile="C:\CC.NET\Server\Test\PackageDirectory\WcfService\*.svc" targetFolder="WcfService" /> <packageFile sourceFile="C:\CC.NET\Server\Test\PackageDirectory\WcfService\*.Release.config" targetFolder="WcfService" /> <packageFile sourceFile="C:\CC.NET\Server\Test\PackageDirectory\WcfService\bin\*.dll" targetFolder="WcfService\bin" /> <!--<packageFolder sourceFolder="C:\CC.NET\Server\Test\PublishDirectory\WcfService" targetFolder="WcfService" fileFilter="*.*" flatten="false" includeSubFolders="false" />--> </packageList> </package> </tasks> <state type="state" directory="C:\CC.NET\server\CCState"/> <publishers> <!--標簽備份(如果成功)--> <buildpublisher> <sourceDir>C:\CC.NET\Server\Test\WorkingDirectory</sourceDir> <publishDir>C:\CC.NET\Server\Test\HistoryVersion</publishDir> </buildpublisher> <modificationHistory/> <statistics/> <!--郵件通知--> <email mailhost="smtp.live.com" mailport="25" mailhostUsername="ci@XXXCompany.com" mailhostPassword="******" from="ci@XXXCompany.com" useSSL="TRUE" includeDetails="true"> <!--郵件標題配置--> <subjectPrefix>[CI@XXXCompany]</subjectPrefix> <subjectSettings> <!-- Success/Broken/StillBroken/Fixed/Exception--> <subject buildResult="Success" value="${CCNetProject} Build Successful: Label ${CCNetLabel}, last checkin(s) by ${CCNetModifyingUsers}.(at ${CCNetBuildDate} ${CCNetBuildDate})" /> <subject buildResult="Fixed" value="${CCNetProject} Build Fixed: Label ${CCNetLabel}, last checkin(s) by ${CCNetModifyingUsers}.(at ${CCNetBuildDate} ${CCNetBuildDate})" /> <subject buildResult="Broken" value="${CCNetProject} Broke: last checkin(s) by ${CCNetFailureUsers}.(at ${CCNetBuildDate} ${CCNetBuildDate})" /> <subject buildResult="StillBroken" value="${CCNetProject} Still Broken: last checkin(s) by ${CCNetFailureUsers}.(at ${CCNetBuildDate} ${CCNetBuildDate})" /> <subject buildResult="Exception" value="${CCNetProject} In Exception: Please check status of network / sourcecontrol.(at ${CCNetBuildDate} ${CCNetBuildDate})" /> </subjectSettings> <!--收件人配置--> <converters> <regexConverter find="$" replace="@XXXCompany.com"/> </converters> <modifierNotificationTypes> <notificationType>Failed</notificationType> <notificationType>Fixed</notificationType> </modifierNotificationTypes> <users> <user group="leader" name="ci.XXXCompany" address="ci@XXXCompany.com"/> <user group="developer" name="harvey.choi" address="harvey.choi@XXXCompany.com"/> <user group="tester" name="jolin" address="jolin.tang@XXXCompany.com"/> </users> <groups> <group name="leader"> <notifications> <!--Always/Success/Change/Fixed/Failed --> <notificationType>Change</notificationType> <notificationType>Exception</notificationType> </notifications> </group> <group name="developer"> <notifications> <notificationType>Success</notificationType> <notificationType>Fixed</notificationType> <notificationType>Failed</notificationType> </notifications> </group> <group name="tester"> <notifications> <notificationType>Fixed</notificationType> </notifications> </group> </groups> </email> <xmllogger/> </publishers> </project> </cruisecontrol>

 

到這里,一個簡單場景的自動化發布部分已經實現。TODO 列表里仍須解決的幾個主要問題:

1.Build 成功,人工測試未通過時,如何一鍵回滾

2.數據庫的更新和回滾如何實現自動化


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM