ClickOnce and Nant – The Plot Thickens

Turns out that these ClickOnce deployment builds aren’t as piss-easy as I once thought.
Turns out that the builds need to be customised for different environments, nothing new there, but (and here comes the catch), all the environmental settings have to be applied at BUILD TIME!!! Why? I hear you ask, and the answer is: because if you edit the config files post-build it changes some checksum jiggery pokery wotnot and then the thingumyjig goes and fails!!! Typical. (Basically the details you need to configure are held in files you cannot edit post build because the manifest file will do a checsum evaluation and see that someone has edited the file, and throw errors).
So what I’ve decided to do is this….

  1. Copy all configurable files to seperate environmentally named folders pre-build.
  2. Use SED to replace tokens for each environment in these files
  3. Copy 1 of them back to the build folder
  4. Compile
  5. Copy the output to the environmentally named folder

redo steps 3,4,5 for all the environments.
And hey presto, this works.

<target name=”changeconfigs”>
<!–This bit sets up some folders where I’ll do the prep work for each environment–>
<delete dir=”${config.dir}\${}” verbose=”true” if=”${directory::exists(config.dir+’\’}” />
<mkdir dir=”${config.dir}\${}\TestArea” />
<mkdir dir=”${config.dir}\${}\DevArea” />
<mkdir dir=”${config.dir}\${}\Staging” />
<mkdir dir=”${config.dir}\${}\UAT” />
<mkdir dir=”${config.dir}\${}\Live” />

<!–This bit moves a tokenised config file to these folders–>
<copy file=”${source.dir}\App_Master.config” tofile=”${config.dir}\${}\TestArea\app.config” />
<copy file=”${source.dir}\App_Master.config” tofile=”${config.dir}\${}\DevArea\app.config” />
<copy file=”${source.dir}\App_Master.config” tofile=”${config.dir}\${}\Staging\app.config” />
<copy file=”${source.dir}\App_Master.config” tofile=”${config.dir}\${}\UAT\app.config” />
<copy file=”${source.dir}\App_Master.config” tofile=”${config.dir}\${}\Live\app.config” />

<!–This bit calls sed, which replaces the tokens with relevant values for each environment, more on sed another time!–>
<exec program=”${sedUAT.exe}” commandline=”${sedParse.dir}” />
<exec program=”${sedTestArea.exe}” commandline=”${sedParse.dir}” />
<exec program=”${sedDevArea.exe}” commandline=”${sedParse.dir}” />
<exec program=”${sedStaging.exe}” commandline=”${sedParse.dir}” />
<exec program=”${sedLive.exe}” commandline=”${sedParse.dir}” />

<!–This bit copies the edited file back to the build directory–>
<target name=”prepTestArea”>
<delete file=”${source.dir}\app.config” />
<copy file=”${config.dir}\${}\TestArea\app.config” tofile=”${source.dir}\app.config” />

<!–This bit builds the ClickOnce project–>
<target name=”publishTestArea” >
<msbuild project=”${base.dir}\Proj1\ClickOnce.vbproj”>
<arg value=”/t:Rebuild” />
<arg value=”/property:Configuration=Release”/>
<arg value=”/p:ApplicationVersion=${version.num}”/>
<arg value=”/p:InstallUrl=http://testarea/ClickOnce/”/>
<arg value=”/t:publish”/>
<arg value=”/p:UpdateRequired=true”/>
<arg value=”/p:MinimumRequiredVersion=${version.num}”/>

<!–This bit copies the output to an environment-named folder, ready for deployment–>
<target name=”copyfilesTestArea”>
<mkdir dir=”${versioned.dir}\TestArea” />
<copy todir=”${versioned.dir}\TestArea” includeemptydirs=”true”>
<fileset basedir=”${base.dir}\Proj1\bin\Release\”>
<include name=”**.publish\**\*.*” />


Now that wasn’t too hard, and it doesn’t take up too much extra time.
I suppose I’d better mention some of the arguments I’m passing in the MSBuild calls:

<arg value=”/t:Rebuild” /> – I do this because it must re build the .deploy files each time, or you get the previous builds environment settings left in there because MSBuild decides to skip files that haven changed….

<arg value=”/property:Configuration=Release”/> – Obvious

<arg value=”/p:ApplicationVersion=${version.num}”/> – ClickOnce apps have a version stamped on them for various reasons, one of them being for use in automatic upgrades – people with installshield knowledge will know what a joke that can be!

<arg value=”/p:InstallUrl=http://testarea/ClickOnce/”/> – A pretty important one this, it stamps the URL for the download onto the manifest or application file.

<arg value=”/t:publish”/> – just calls the publish task, I do this because this makes the setup.exe

<arg value=”/p:UpdateRequired=true”/>
<arg value=”/p:MinimumRequiredVersion=${version.num}”/> – These 2 together mean the app will do a forced upgrade when a new version becomes available

So far, so good. My next trick will hopefully be how to get 2 installations working side-by-side. Currently it doesn’t work because one will overwrite the other. I’m working on it okay!!??



  1. Greg Jackman · April 22, 2011

    Hi James. Great blog post. Configuring multiple environments always causes a headache!

    You should check out ClickOnceMore. I built it for exactly this sort of situation. Its a tool for building ClickOnce manifests outside of VS or Mage. Its targetted at supporting complex build situations using macros. You can embed macros in your config file and then just run ClickOnceMore from the command line using a different set of macro values. Its pretty powerful. You can trial it from

    Greg Jackman
    Red Sky Software

    • Choowei · January 22, 2013

      您好,來冒昧請問問題:狀況是這樣的,我目前有兩個站一舊(A)一新(B),我把A的資料庫下載備份並且重新放入空的資料庫當作B的data,再把A主機目錄底下的東西附製到B的目錄下。安裝上去後文章連結等等都沒有問題,就是前台變成亂碼。照您這篇的方式我修改過sql檔案,sql檔案在A匯出來時本身指定語系的就是utf8了(不過檔案開出來的內容都是??只是我找到的指定的那幾行與法是定義utf8沒錯)然後/星號!40101 SET NAMES latin1 星號/;這個部份我也改成utf8,重新放上資料庫後,檢察wp-config.php裡,define( DB_CHARSET’, utf8 ); 跟 define( DB_COLLATE’, utf8_general_ci’); 設定也都沒錯(連線校正是utf8_general_ci)。結果B站前台還是??唉。感覺我能做的都做了。卻還是無法除錯。我在想問題會不會是出在A備份下來的sql檔,疑點大概是:1)A的資料庫我進去後發現連線校正的設定是原本的(拉丁1然後瑞典的樣子),所以我想可能是因為這原因所以下載的data是錯的,所以我將它設定成utf8_general_ci再下載後打開,內容卻還是都是??(不過我有檢察 資料本身 的連線校正是utf8_general_ci);上傳B資料庫之後B前台也還是問號。問題無法解決 2)另外請問如果是正常的sql檔案,下載後打開該是中文的部分會是中文嗎?如果是的話就有爆點了XD 因為A下載下來的sql檔打開就是問號啊啊。感覺我能做的都做了,檢查過wp-config.php裡也沒有問題,不知道這樣子您是否能幫忙看出問題在哪呢?先謝謝您喔。

  2. Neil Squires · July 17, 2012


    Nice post. It is a quite frustrating when trying to design a deployment pipeline that uses the same binary files for all the different environments we need. I wish ClickOnce played a little nicer when trying to set this up.

    I also noticed that you mentioned in your article that you had not figured out how to get multiple environments installed side by side. I found an article that I think my help you in this if you are still interested. It’s on Lars Andreas Ek’s Blog

    Thanks for sharing your code.

    Neil Squires

  3. Keiko · January 22, 2013

    你好,如果不想改(怕自己手賤改壞),我想重開一個新資料庫+重po之前的文章資料,請問: -國外主機代理商提供的版本資料如下:Apache veriosn: 1.3.41 (Unix)PHP veriosn: 5.2.5MySQL veriosn: 5.0.81-communityOperating system: Linux使用cPanel 來管理 用cPanel 進入phpMyAdmin時的第一頁,顯示 * 伺服器版本: 5.0.81-community * 通訊協定版本: 10 * MySQL 文字編碼: UTF-8 Unicode (utf8) * MySQL 連線校對: utf8_unicode_ci我按資料庫選項時出現:新資料庫B 校對:utf8_unicode_ci舊資料庫A 校對:latin1_swedish_ci其它資料庫C 校對:utf8_general_ci總共: 3 latin1_swedish_ci(新資料庫B是新開的,要給wordpress2.8.2使用)(舊資料庫A是wordpress2.7使用)(其它資料庫C不是我建的,名稱是information之類的,不知為何它就在那了,)新資料庫B我重開後,它先顯示latin1_swedish_ci,我去 管理/校對 將它改成:utf8_unicode_ci這樣子我上傳wordpress2.8.2後,在wp-config.php

  4. Justine · May 7, 2013

    It is appropriate time to make some plans for the future and it’s time to be happy. I have read this post and if I could I want to suggest you few interesting things or tips. Perhaps you could write next articles referring to this article. I desire to read more things about it!

  5. cars · June 23, 2013

    A little bit of knowledge goes a long way in all situations in life.
    Buying a car is no different! That means you need to read advice from experts, as detailed
    below, to ensure that when you shop for that car, you really know what you’re doing and how to get the best deal.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s