- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Saturday, February 18, 2012

How to create a Web Deploy package when publishing a ClickOnce project

The other day I saw a question on StackOverflow (link in resources below) asking How you can create a Web Deploy (AKA MSDeploy) package when publishing a ClickOnce project. The easiest way to do this is to use the Web Deploy command line utility, msdeploy.exe. With the command line you can easily create an MSDeploy package from a folder with a command like the following:

    %msdeploy% 
      -verb:sync 
      -source:contentPath="C:\Temp\_NET\WebPackageWithClickOnce\WebPackageWithClickOnce\bin\Debug\app.publish" 
      -dest:package="C:\Temp\_NET\WebPackageWithClickOnce\WebPackageWithClickOnce\bin\Debug\co-pkg.zip"

 

Here you can see that I’m using the sync verb, along with a contentPath provider (which points to a folder) as the source and the destination is using the package provider, this point to where I want the package to be stored.

Now that we understand how to create an MSDeploy package from a folder we need to extend the ClickOnce publish process to create a package. I’m not a ClickOnce expert, but the ClickOnce publish process is captured in MSBuild so after investigating for a bit I found the following relevant details.

Now that we know what target to extend as well as what property we can use to refer to the folder which has the content we can complete sample. We need to edit the project file. Below is the full contents which I have placed at the bottom of the project file (right above </Project>).

  <PropertyGroup>
    <WebDeployPackageName Condition=" '$(WebDeployPackageName)'=='' ">$(MSBuildProjectName).zip</WebDeployPackageName>
    <!--Unless specified otherwise, the tools will go to HKLM\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\1 to get the installpath for msdeploy.exe.-->
    <MSDeployPath Condition="'$(MSDeployPath)'==''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\3@InstallPath)</MSDeployPath>
    <MSDeployPath Condition="'$(MSDeployPath)'==''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\2@InstallPath)</MSDeployPath>
    <MSDeployPath Condition="'$(MSDeployPath)'==''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\1@InstallPath)</MSDeployPath>
    <MSDeployExe Condition=" '$(MSDeployExe)'=='' ">$(MSDeployPath)msdeploy.exe</MSDeployExe>
  </PropertyGroup>
  <Target Name="CreateWebDeployPackage" AfterTargets="Publish" DependsOnTargets="Publish">
    <!--
    %msdeploy% 
      -verb:sync 
      -source:contentPath="C:\Temp\_NET\WebPackageWithClickOnce\WebPackageWithClickOnce\bin\Debug\app.publish" 
      -dest:package="C:\Temp\_NET\WebPackageWithClickOnce\WebPackageWithClickOnce\bin\Debug\co-pkg.zip"
      -->
    <PropertyGroup>
      <Cmd>"$(MSDeployExe)" -verb:sync -source:contentPath="$(MSBuildProjectDirectory)\$(PublishDir)" -dest:package="$(OutDir)$(WebDeployPackageName)"</Cmd>
    </PropertyGroup>
    <Message Text="Creating web deploy package with command: $(Cmd)" />
    <Exec Command="$(Cmd)" />
  </Target>

Here I’ve created a couple properties as well as a new target, CreateWebDeployPackage. I have declared the property WebDeployPackageName which will be the name (excluding path) of the Web Deploy package which gets created. This defaults to the name of the project, but you can override it if you want. Next I define the property, MSDeployPath, which points to msdeploy.exe. It will pick the latest version.

The CreateWebDeployPackage target just constructs the full command line call which needs to be executed and invokes it using the Exec MSBuild task. There are a couple subtle details on the target itself though which are worth pointing out. The target has declared AfterTargets=”Publish” which means that it will be invoked after the Publish target. It also declares DependsOnTargets=”Publish”. Which means that whenever the target gets invoked that Publish will need to be executed before CreateWebDeployPackage.

Now that we have defined these updates when you publish your ClickOnce project (wither through Visual Studio or the command line/build servers) a Web Deploy package will be generated in the output folder which you can use to incrementally publish your ClickOnce app to your web server. You can find the latest version of this sample on my github repository.

Sayed Ibrahim Hashimi @SayedIHashimi

Resources

ClickOnce | IIS | Microsoft | msbuild | MSDeploy | web Saturday, February 18, 2012 6:47:30 PM (GMT Standard Time, UTC+00:00)  #     | 
Tuesday, February 14, 2012

How to update a single file using Web Deploy (MSDeploy)

The other day I saw a question posted on StackOverflow (link to question below in resources section) asking if it was possible to update web.config using MSDeploy. I actually used a technique where I updated a single file in one of my previous posts at How to take your web app offline during publishing but it wasn’t called out too much. In any case I’ll show you how you can update a single file (in this case web.config) using MSDeploy.

You can use the contentPath provider to facilitate updating a single file. Using contentPath you can sync either a single file or an entire folder. You can also use IIS app paths to resolve where the file/folder resides. For example if I have a web.config file in a local folder named “C:\Data\Personal\My Repo\sayed-samples\UpdateWebConfig” and I want to update my IIS site UpdateWebCfg running in the Default Web Site on my folder I would use the command shown below.

%msdeploy% -verb:sync -source:contentPath="C:\Data\Personal\My Repo\sayed-samples\UpdateWebConfig\web.config" -dest:contentPath="Default Web Site/UpdateWebCfg/web.config"

From the command above you can see that I set the source content path to the local file and the dest content path using the IIS path {SiteName}/{AppName}/{file-path}. In this case I am updating a site running in IIS on my local machine. In order to update one that is running on a remote machine you will have to add ComputerName and possibly some other values to the –dest argument.

You can view the latest sources for this sample at my github repo, link is below.

 

Hope that helps!

Sayed Ibrahim Hashimi – @SayedIHashimi

Resources:

IIS | MSDeploy | web | Web Publishing Pipeline Tuesday, February 14, 2012 7:17:30 PM (GMT Standard Time, UTC+00:00)  #     | 
Saturday, February 04, 2012

SlowCheetah XML Updates for Setup projects, ClickOnce, F# and more

I blogged in December about SlowCheetah which is a Visual Studio add in which can be used to transform app.config (or any XML file for that matter) in ways similar to web.config. A few days back we released an update to this adding with a few key items which are listed below.

  1. Support for ClickOnce
  2. Support for Setup projects
  3. Support for F# projects
  4. Support for a custom diff viewer
  5. Support for XP/Win server 2003
  6. Better error handling, logging

I’ve created a set of samples for SlowCheetah which I’ll use to describe these new features. Download links for the samples, as well as pointers to the latest versions are at the bottom of this page.

Support for ClickOnce

If you are building a windows client application (Win forms or WPF) then you can use ClickOnce to publish this application. When you publish a project using ClickOnce a setup.exe will be generated as well as an HTML page which can be served by a web server to enable users to download it. When this setup.exe is generated all of the files needed to run your application (including app.config) are placed inside of that executable. When you have a project using SlowCheetah and you publish using a specific configuration it will now publish the transformed app.config (as well as any other transformed files).

In order to demonstrate this I’ve created a sample WPF application which I will publish with ClickOnce (download link is at the bottom). This project has an app.config file as well as app.Debug.config and app.Release.config.

Below is what the original app.config file looks like.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="appName" value="WPF Demo-Debug-default"/>
    <add key="url" value="http://localhost:8080/Default/"/>
    <add key="email" value="demo-default@contoso.com"/>
  </appSettings>
  
  <connectionStrings>
    <clear />
    <add name="RecordsDb" connectionString=".\SQLExpress;Initial Catalog=RecordsDb-Default;Integrated Security=true"/>  
  </connectionStrings>
  
</configuration>

And here is app.Debug.Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  
  <appSettings>
    <add key="appName" value="WPF Demo-Debug" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
    <add key="url" value="http://localhost:8080/" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
    <add key="email" value="debug@contoso.com" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
  </appSettings>

  <connectionStrings>
    <add name="RecordsDb" connectionString=".\SQLExpress;Initial Catalog=RecordsDb;Integrated Security=true"
         xdt:Transform="Replace" xdt:Locator="Match(name)"/>
  </connectionStrings>
  
</configuration>

ddd

As you can see I am transforming both the appSettings as well as the connection string. The application itself is pretty simple, it has a single page which shows the following values.

When I run this application in Debug mode I will get the following result.

image

In previous versions of SlowCheetah when you published your application with ClickOnce the original version of app.config was getting published instead of the transformed one. This was actually a bug in the .targets files for ClickOnce, but we have now worked around it in the SlowCheetah .targets files. So let’s say that you have a ClickOnce application and you need to publish different versions which have config changes then you can now easily do that with SlowCheetah.

Support for Setup projects

Visual Studio 2010 also has Setup projects, which can be used to generate an MSI. When you create the MSI you can specify that it includes the outputs of a project in the solution. We had a similar story here in which the original app.config was being placed into the MSI instead of the transformed on in the output folder. We have now fixed that and if you have a Setup project which reference a project with SlowCheetah transforms then the transformed files will endup in the resulting MSI instead of the original one.

Support for F# projects

If you have F# project and tried SlowCheetah you probably noticed that the “Add Transforms” menu item didn’t show up for those project types. They now do.

image

After you invoke this command you will see a transform created for each configuration defined for the project. In other project types these transform appear as child items, but F# projects don’t support this in the same way as C#/VB projects so they will not be nested. So they will show up as the following.
image

I created a very simple F# application to demonstrate the behavior, below is the code.

// A simple application to create the config and show results in the console
let settingOne = System.Configuration.ConfigurationManager.AppSettings.["settingOne"];
let settingTwo = System.Configuration.ConfigurationManager.AppSettings.["settingTwo"];
let settingthree = System.Configuration.ConfigurationManager.AppSettings.["settingThree"];


printfn "settingOne: %s" settingOne
printfn "settingTwo: %s" settingTwo
printfn "settingThree: %s" settingthree

printfn " "
printfn "Press any key to close"
System.Console.ReadKey(true)

As you can see this app just reads a few config values from app.config and displays them on the console. Here is the original app.config file.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="settingOne" value="one default value"/>
    <add key="settingTwo" value="two default value"/>
    <add key="settingThree" value="three default value"/>
  </appSettings>
</configuration>

Here is app.Debug.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings>
    <add key="settingOne" value="one Debug"
         xdt:Locator="Match(key)" xdt:Transform="Replace" />
    
    <add key="settingTwo" value="two Debug"
         xdt:Locator="Match(key)" xdt:Transform="Replace" />
    
    <add key="settingThree" value="three Debug"
         xdt:Locator="Match(key)" xdt:Transform="Replace" />
  </appSettings>
</configuration>

There is a similar app.Release.config as well for this project.

When I run this in Debug mode below is the result

image

When I run this in Release mode below is the result.

image

As you can see F# project will now pickup up the transformed web.config file instead of the original one when running.

Support for a custom diff viewer

As you may know SlowCheetah has a Preview Transform functionality that allows you to quickly see the difference between the source file and the transformed one. For example in my Wpf.Transform project I have a app.config file and app.Debug.config when I select one of the transforms I can see this menu item.

image
When that menu item is invoked we open the Visual Studio diff viewer to show you the difference between these two files. We have received a number of requests to support different diff viewers and we have enabled this in the latest release.

After installing SlowCheetah you can go to Tools->Options and then pick SlowCheetah on the left hand side. You will see the following.

image

Here we have three settings which you can customize, they are outlined below.

Setting

Description

Preview Tool Command Line This is a string.Format string that should be used to construct the command to be executed. Here {0} will be replaced with the full path to the original file and {1} with the full path to the transformed file.
Preview Tool Executable Path This is the full path to the .exe which should be invoked during preview.
The default value for this is “C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\diffmerge.exe” which is the diff viewer that VS uses by default.
Run Preview Tool If this is set to False then a diff viewer will not be shown, instead the transformed file will just be opened.

If you want to use KDiff with SlowCheetah then you need it to invoke the following command:

“C:\Program Files (x86)\KDiff3\kdiff3.exe” {Path-to-original-file} {Path-to-transformed-file}

So in this case I just have to update the value for Preview Tool Executable Path to be C:\Program Files (x86)\KDiff3\kdiff3.exe. After that when I perform a transform I will see something like.

image

Support for XP/Win server 2003

After publishing the original version of SlowCheetah users started complaining that it didn’t work for either Windows XP or Windows Server 2003. This is because SlowCheetah writes file under the %LocalAppData% folder. What I didn’t realize was that XP and Win server 2003 don’t have this environment variable defined! Because of this I had to update the logic of where to write the files if that environment variable didn’t exist. Now everything works seamlessly.

Better error handling, logging

We had some issues in previous builds and they were difficult to diagnose because we didn’t have any logging support. We have now updated this and we should be able to diagnose user issues much easier.

 

Below are a list of resources and pointers, I hope that you guys like these updates and please do continue giving us feedback on this!

Resources

Sayed Ibrahim Hashimi @SayedIHashimi

Note: I work for Microsoft but this add in was not created nor is supported by Microsoft.

Saturday, February 04, 2012 8:14:30 PM (GMT Standard Time, UTC+00:00)  #     |