Third Shelf

Automating a ClickOnce Deployment – Part3

Posted in team foundation server by Sydney du Plooy on March 20, 2010

Continuing the series on Automating a ClickOnce Deployment it’s time to implement the deployment scheme in a build file.

After the build file compiled the solution we are ready to start the copying of the installation files to the deployment server. Before we start the copy process we have to create a new directory for the new application version. In the following snippet I simply generate a new version number and then create a directory where we will copy the installation files to:

<Target Name="DeployApplication">
  <Message Text="Deploying the client application to $(PublishServer)" />
  <MakeDir Directories="$(DeploymentServer)\Published\App\$(VersionNumber)" />

We are ready to copy the application files to the server:

<Message Text="Copying files to deployment directory [$(DeploymentServer)\Published\App\$(VersionNumber)]" />
<Copy DestinationFiles="@(ApplicationFiles->'$(DeploymentServer)\Published\App\$(VersionNumber)\%(Filename)%(Extension)')" SourceFiles="@(ApplicationFiles->'$(BinariesFolder)\%(Filename)%(Extension)')" />

The ApplicationFiles is an item group with the installation files that we are copying. Note: we do not transform the filename to include the .deploy extension because we first need to generate the deployment manifest with the files in the right location before changing the name of the files. (See Gotcha #3). Next, we create the application manifest with the Mage utility which will be stored in the application directory (not in the version directory). Mage is a manifest generator and is used to generate both application and deployment manifests. In the snippet below we are using Mage explicitly to generate the application manifest:

<CreateProperty Value="$(DeploymentServer)\Published\App\$(VersionNumber)\App.exe.manifest">
  <Output TaskParameter="Value" PropertyName="ApplicationManifestFile"/>
</CreateProperty>
<Message Text="Generating application manifest  [$(ApplicationManifestFile)] using  [$(DeploymentServer)\Published\App\$(VersionNumber)]" />
<Exec Command="mage.exe -New  Application -p msil -TrustLevel FullTrust -ToFile  $(ApplicationManifestFile) -Name  &quot;App&quot; -Version $(VersionNumber) -FromDirectory  $(DeploymentServer)\Published\App\$(VersionNumber)" />

So far so good, we have now managed to generate the deployment manifest from the files located in the …\Published\App directory and placed the file in the …\Published\App\$(VersionNumber) directory. We will now point the application manifest to this deployment manifest so that the correct files are installed on the client machine.

We now have everything that we need in order to generate the application manifest.

<CreateItem Include="$(ApplicationManifestFile)" AdditionalMetadata="TargetPath=$(VersionNumber)\App.exe.manifest">
  <Output TaskParameter="Include" ItemName="RelativeApplicationManifestFile"/>
</CreateItem>

<GenerateDeploymentManifest AssemblyName="App.exe.application"
  AssemblyVersion="$(VersionNumber)"
  DeploymentUrl="http://deployment-server/Published/App/App.exe.application"
  Product="App"
  Description="App"
  Publisher="thirdshelf.com"
  Install="true"
  UpdateEnabled="true"
  UpdateMode="Foreground"
  OutputManifest="$(DeploymentServer)\Published\App\App.exe.application"
  MapFileExtensions="true"
  EntryPoint="@(RelativeApplicationManifestFile)" />

If you read part 2 of this series you should be able to map these properties straight to their positions in the application manifest file and their functions. The only thing that remains is to rename the files to include their .deploy extensions. This can easily be done in the following way:

<Message Text="Replacing files in [$(DeploymentServer)\Published\App\$(VersionNumber)\] with [.deploy]" />
<Delete Files="@(ApplicationFiles->'$(DeploymentServer)\Published\App\$(VersionNumber)\%(Filename)%(Extension)')" />
<Copy SourceFiles="@(ApplicationFiles->'$(BinariesFolder)\%(Filename)%(Extension)')
  DestinationFiles="@(ApplicationFiles->'$(DeploymentServer)\Published\App\$(VersionNumber)\%(Filename)%(Extension).deploy')" />

Visiting the URL http://deployment-server/Published/App/App.exe.manifest and clicking on the Install button you should see the following dialog:

Gotchas

There are a few gotchas that you might find on your way to success and I’ve listed the most common ones here and their solutions for your convenience:

Gotcha #1: Reference in the manifest does not match the identity of the downloaded assembly …

This is easily resolved by adding the NoWin32Manifest property to solution in the SolutionsToBuild group:

<SolutionToBuild Include="$(SolutionRoot)\Application.sln">
  <Properties>NoWin32Manifest=true</Properties>
</SolutionToBuild>

Gotcha #2: XML files are marked as data files, by default.

This means that XML files are published to the data directory whose location can found by querying the System.Deployment.Application.ApplicationDeployment.CurrentDeployment.DataDirectory  property.

Gotcha #3: The customHostSpecified attribute is not supported for Windows Forms applications.

Not a very helpful error, but chances are that you generated the deployment manifest with the installation files having the .deploy extension and not on the original filenames.

Tagged with: , ,

3 Responses

Subscribe to comments with RSS.

  1. Bhavesh said, on April 7, 2010 at 11:38

    I’m not sure how can I solve Gotcha #3: The customHostSpecified attribute is not supported for Windows Forms applications.

    My project has references to several other projects. Through TFSBuild I am able to generate .application and .manifest files but still I get this error.

    Strange but I do not get any .deploy files generated after running

  2. Springy said, on April 7, 2010 at 12:52

    addendum regarding Gotcha #3:
    Chances are that your main exe is not an .NET assembly at all — or you are using mage 2.0/3.5 against an 4.0 application

  3. Michael Caron said, on March 30, 2011 at 13:35

    How do you deal with a signing certificate in the automated build? How does MSBuild know what the password to the *.pfx file is so it can build the code?


Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 144 other followers