Posts Tagged ‘ Publish ’

ClickOnce Publish to Web with MSBuild

Summary

With ClickOnce you may publish to a web or file share location. This MSDN article tells you how to do it from Visual Studio, but doing it from VS is very limiting what you will end up wanting to do is publish using MsBuild from the command prompt.  This posting is not a comprehensive ‘how-to’ but has some useful tips, simple but painful lessons learnt through trial and error.

Project File Changes

Open up your Project.csproj in a text editor (I always use notepad++). Set these properties, add them if they are not there already….they should be near the top of the file and probably not together.

 <IsWebBootstrapper>true</IsWebBootstrapper>
 <InstallFrom>Web</InstallFrom>
 <PublishDir>C:\Dev\Release\$(BuildEnvironment)\</PublishDir>

InstallFrom

As far as I can tell this can either be Web or Unc

IsWebBootstrapper

This one is really important, if you do not set this to true the publish will work fine but you will have problems installing. If you see an error that looks something like this

SOURCES
 Deployment url            : file:///C:/Documents%20and%20Settings/Administrator.HP275/Local%20Settings/Temporary%20Internet%20Files/Content.IE5/QTJPP0ZG/MyApp.Shell.application

ERROR SUMMARY
 Below is a summary of the errors, details of these errors are listed later in the log.
 * Activation of C:\Documents and Settings\Administrator.HP275\Local Settings\Temporary Internet Files\Content.IE5\QTJPP0ZG\MyApp.Shell.application resulted in exception. Following failure messages were detected:
 + Downloading file:///C:/Documents and Settings/Administrator.HP275/Local Settings/Temporary Internet Files/Content.IE5/QTJPP0ZG/MyApp.Shell.application did not succeed.

the likely cause of the problem is the IsWebBootstrapper value is false. Note that VS kindly changes this value for you sometimes…so be careful! The clue that this is the cause is of course the fact that the installer is trying to find the file from a local path.

PublishDir

This is still a unc path, preferably to the dir of the virtual directory in IIS. However if the machine you are publishing from does not have write access to that location on the network, you can publish to a local location then copy the files to the secure machine.

Batch File

I like to create a separate batch file for each environment that I need to publish to, e.g. QA, Training, Production etc.

The reason is, SOME of the MsBuild variables that need to be set must be passed in (as far as I can tell) from the command prompt to MsBuild.

So this is what my batch files look like (check out my posting on MsBuild and multiple environments for details on the extra properties).

msbuild /t:Publish /p:Configuration=Release /p:BuildEnvironment=QA /p:ApplicationVersion=1.0.3.67 /p:UpdateUrl=http://MachineNameOrIP/Release/QA/ /p:InstallUrl=http://MachineNameOrIP/Release/QA/

InstallUrl and UpdateUrl are both properties which apparently need to be passed in as parameters which is why I put them in the batch file.

The ApplicationVersion I still do manually because I haven’t tried setting up the automated solution yet.

IIS

To publish from a web address you will need to setup a virtual directory in IIS.

The main ‘Gotcha’ I encountered was setting the execute permissions to Scripts and executables. It must be Scripts only! This post help me figure out what the hell was going wrong when I had the wrong setting.

Final Note

If you are publish to the web then you most likely want/need a publish page, check out my other posting on creating the publish.html file as part of your build (this file gets created when you publish from VS but you stop getting it as soon as you publish directly from MsBuild)

Happy publishing 🙂

ClickOnce – Exception reading manifest from file

the manifest may not be valid or the file could not be opened

This is a problem that you can run into when you publish an application using ClickOnce.

The error message provided by ClickOnce is a little obscure and there may well be a number of different causes, this posting is about what was causing the problem in my case.

This is a more complete look at the error

What Causes the Problem?

Multiple references to the same dll(s) (not necessarily just a project reference).

In our case we were pulling in non .net dll’s from two different directories that each contained about 5 dll’s that existed in both directories. Same version and everything, versioning was not causing the problem.

As soon as we removed the duplicates the problem went away.

We also experienced the same problem when we were pulling a configuration file explicitly from one location but had also added the file to the main project.  So once again because a file with the same name was ‘included’ in the publish, ClickOnce throws an error because the manifest is invalid.

This guy found the problem was due to an Enterprise Library dll. I don’t know the details but I’d be willing to bet that he had a reference to two different versions of the same dll in two different projects.

Summary

While it would be nice to get a clearer error message I think what ClickOnce is doing here is pretty nice. It is making sure that you don’t get into any trouble with different references to the same file…which could be a different version and give you a world of subtle, very difficult to find bugs.

I hope this makes sense…it has been written very quickly 🙂

ClickOnce Deployment Error

Full Error

See this posting for description of thi error and what the problem was….

PLATFORM VERSION INFO
Windows             : 5.1.2600.196608 (Win32NT)
Common Language Runtime     : 2.0.50727.3603
System.Deployment.dll         : 2.0.50727.3053 (netfxsp.050727-3000)
mscorwks.dll             : 2.0.50727.3603 (GDR.050727-3600)
dfdll.dll             : 2.0.50727.3053 (netfxsp.050727-3000)
dfshim.dll             : 2.0.50727.3053 (netfxsp.050727-3000)

SOURCES
Deployment url            : file:///C:/Dev/Release/DEV/MyApp.application

IDENTITIES
Deployment Identity        : MyApp.Shell.application, Version=1.2.3.5, Culture=neutral, PublicKeyToken=12ebb91613a16909, processorArchitecture=msil

APPLICATION SUMMARY
* Installable application.

ERROR SUMMARY
Below is a summary of the errors, details of these errors are listed later in the log.
* Activation of C:\Dev\Release\DEV\MyApp.Shell.application resulted in exception. Following failure messages were detected:
+ Exception reading manifest from file:///C:/Dev/Release/DEV/Application%20Files/MyApp.Shell_1_2_3_5/MyApp.Shell.exe.manifest: the manifest may not be valid or the file could not be opened.
+ Parsing and DOM creation of the manifest resulted in error. Following parsing errors were noticed:
-HRESULT:     0x80070c89
Start line:     0
Start column:     0
Host file:
+ Master  did not send an update notice for directory  at the expected time. (Exception from HRESULT: 0x80070C89)

COMPONENT STORE TRANSACTION FAILURE SUMMARY
No transaction error was detected.

WARNINGS
There were no warnings during this operation.

OPERATION PROGRESS STATUS
* [29/12/2009 2:02:27 PM] : Activation of C:\Dev\Release\DEV\MyApp.Shell.application has started.
* [29/12/2009 2:02:30 PM] : Processing of deployment manifest has successfully completed.
* [29/12/2009 2:02:30 PM] : Installation of the application has started.

ERROR DETAILS
Following errors were detected during this operation.
* [29/12/2009 2:02:30 PM] System.Deployment.Application.InvalidDeploymentException (ManifestParse)
– Exception reading manifest from file:///C:/Dev/Release/DEV/Application%20Files/MyApp.Shell_1_2_3_5/MyApp.Shell.exe.manifest: the manifest may not be valid or the file could not be opened.
– Source: System.Deployment
– Stack trace:
at System.Deployment.Application.ManifestReader.FromDocument(String localPath, ManifestType manifestType, Uri sourceUri)
at System.Deployment.Application.DownloadManager.DownloadManifest(Uri& sourceUri, String targetPath, IDownloadNotification notification, DownloadOptions options, ManifestType manifestType, ServerInformation& serverInformation)
at System.Deployment.Application.DownloadManager.DownloadApplicationManifest(AssemblyManifest deploymentManifest, String targetDir, Uri deploymentUri, IDownloadNotification notification, DownloadOptions options, Uri& appSourceUri, String& appManifestPath)
at System.Deployment.Application.ApplicationActivator.DownloadApplication(SubscriptionState subState, ActivationDescription actDesc, Int64 transactionId, TempDirectory& downloadTemp)
at System.Deployment.Application.ApplicationActivator.InstallApplication(SubscriptionState& subState, ActivationDescription actDesc)
at System.Deployment.Application.ApplicationActivator.PerformDeploymentActivation(Uri activationUri, Boolean isShortcut, String textualSubId, String deploymentProviderUrlFromExtension, BrowserSettings browserSettings, String& errorPageUrl)
at System.Deployment.Application.ApplicationActivator.ActivateDeploymentWorker(Object state)
— Inner Exception —
System.Deployment.Application.InvalidDeploymentException (ManifestParse)
– Parsing and DOM creation of the manifest resulted in error. Following parsing errors were noticed:
-HRESULT:     0x80070c89
Start line:     0
Start column:     0
Host file:
– Source: System.Deployment
– Stack trace:
at System.Deployment.Application.Manifest.AssemblyManifest.LoadCMSFromStream(Stream stream)
at System.Deployment.Application.Manifest.AssemblyManifest..ctor(FileStream fileStream)
at System.Deployment.Application.ManifestReader.FromDocument(String localPath, ManifestType manifestType, Uri sourceUri)
— Inner Exception —
System.Runtime.InteropServices.COMException
– Master  did not send an update notice for directory  at the expected time. (Exception from HRESULT: 0x80070C89)
– Source: System.Deployment
– Stack trace:
at System.Deployment.Internal.Isolation.IsolationInterop.CreateCMSFromXml(Byte[] buffer, UInt32 bufferSize, IManifestParseErrorCallback Callback, Guid& riid)
at System.Deployment.Application.Manifest.AssemblyManifest.LoadCMSFromStream(Stream stream)

COMPONENT STORE TRANSACTION DETAILS
No transaction information is available.

ClickOnce – Creating Publish Page from msbuild

ClickOnce via msbuild

The Problem

When you publish from Visual Studio, the publish directory gets three files:-

  • Publish.htm
  • setup.exe
  • MyApp.application

but when you publish using msbuild you do not get the Publish.htm.

This post covers one approach to ensure you also get a nice publish.htm file. Actually it’s a two part blog, in the second part I’ll cover how to simplify your deployment process for multiple environments.

As a pre-requisite you’ll need to install msbuild community tasks.

All credit for this post belongs to my good friend Craig Hunter, this is pretty much a slightly modified version of his solution.

Step One – Create Customization File

This step in kind of optional. All subsequent steps may be done within your .csproj, it is just my preference to put all of my customizations into a separate file.

1. In your project file directory, create a new file called Customized.targets (actually call it whatever you want).
2. Now open your .csproj in notepad++, copy the first and last lines and add them to Customized.targets. Should look something like this

<Project DefaultTargets=”Build” xmlns=”http://schemas.microsoft.com/developer/msbuild/2003&#8243; ToolsVersion=”3.5″>
</Project>

3. At the bottom of your .csproj add these two lines

<Import Project=”Customized.targets” />
<Import Project=”$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets” />

What is not optional is adding the Import of the MSBuild community tasks.

Step Two – Add Publish Template

  1. In your project file directory, add a new directory called Publish.
  2. Add this file to that directory.

You don’t really need to but I also added the directory to my project. At the very least you do have to add it to source control (otherwise it won’t get to the build machine)

Step Three – Customizations

Add these customizations to your Customized.targets file. Remove the numbers, they are just there for reference below…

1.	<PropertyGroup>
		<BuildEnvironment>DEV</BuildEnvironment>
	</PropertyGroup>

2.	<Choose>
		<When Condition=" '$(BuildEnvironment)' == 'DEV' ">
		  <PropertyGroup>
		    <PublishDir>\\MachineIP\ReleaseDirectory\$(BuildEnvironment)\</PublishDir>
		  </PropertyGroup>
		</When>
		<When Condition=" '$(BuildEnvironment)' == 'QA' ">
		  <PropertyGroup>
		    <PublishDir>\\MachineIP\ReleaseDirectory\$(BuildEnvironment)\</PublishDir>
		  </PropertyGroup>
		</When>	</Choose>

3.	<PropertyGroup>
		<!-- Note this must be done AFTER the above Choose (so PublishDir is set)-->
		<PublishFilePath>$(PublishDir)publish.html</PublishFilePath>
	</PropertyGroup>

4.	<ItemGroup>
		<Tokens Include="PublisherName">
		  <ReplacementValue>$(PublisherName)</ReplacementValue>
		  <Visible>false</Visible>
		</Tokens>
		<Tokens Include="ProductName">
		  <ReplacementValue>$(ProductName)</ReplacementValue>
		  <Visible>false</Visible>
		</Tokens>
		<Tokens Include="ApplicationVersion">
		  <ReplacementValue>$(ApplicationVersion)</ReplacementValue>
		  <Visible>false</Visible>
		</Tokens>
		<Tokens Include="Prerequsites">
		  <ReplacementValue>@(BootstrapperPackage->'&lt;li&gt;%(ProductName)&lt;/li&gt;','%0D%0A')</ReplacementValue>
		  <Visible>false</Visible>
		</Tokens>
		<Tokens Include="Username">
		  <ReplacementValue>$(Username)</ReplacementValue>
		  <Visible>false</Visible>
		</Tokens>
	</ItemGroup> 

5.	<Target Name="AfterPublish">
		<Time Format="dd/MM/yyyy HH:mm">
		  <Output TaskParameter="FormattedTime" PropertyName="PublishTime" />
		</Time>
		<!-- Finalise the publish.htm template file and copy it to the publish location -->
		<TemplateFile Template="Publish\publish.template.html" Tokens="@(Tokens)" OutputFilename="$(PublishFilePath)" />
		<FileUpdate Files="$(PublishFilePath)" Regex="\${PublishTime}" ReplacementText="$(PublishTime)" />
	</Target>

1. More on the BuildEnvironment property in a later posting. It’s not essential just remove all references to it if you want to remove it.

2. Also more on this in a later post, it is for specifying details for multiple environments. In the provided sample the PublishDir is the same machine and parent directory for both DEV and QA with a separating sub directory of the environment name. Don’t forget to SHARE the ReleaseDirectory folder!

3. Tells MSBuild where to put the publish.html file (name it what you want)

4. The publish.template.html has a number of tokens which need to be set.  Adjust these how you want, add or remove whatever tokens you want in the template.

5. After MSBuild finishes publishing it will run this section which creates the publish.html file using the token values.

Save those changes and you are done, you should now be able to run MSBuild, call publish and have a publish.html created for you with relevant information.

Step Four – Run MSBuild

Open Visual Studio command prompt, navigate to your project directory, execute (this assumes you only have one project file in the directory):

msbuild /t:Publish /p:Configuration=Release /p:BuildEnvironment=DEV

Done!

Why Bother?

If you get the file from VS why then care about what happens when publishing directly from msbuild?

I can think of three reasons why you need to publish from msbuild.

First, ideally developers should never publish from their machines.  (I say ideally because I have to raise a guilty hand and confess this is currently what I am doing…sometimes cirumstance forces bad practice upon us). This means ideally you have a build machine and it is from this machine only that releases should be done. So if you want to use VS to do the publish then you need to install and licence that machine.

Secondly, you want to be able to do a release from the command line, you don’t want to muck around starting VS, wait for the solution to load, open properties and click publish, what a waste of time, with msbuild you can create a batch file or two and start the publish process in the time it takes you to navigate to that directory.

Finally, the customization options available to you from within VS is too limiting. For example, if you are using VS to publish to multiple environments, how do you handle changing the parameters for each environment?

Some Notes

You can of course still run publish from VS but I don’t recommend it, get out of that bad habit and start using MSBuild directly. However if you insist on using VS, if you make any changes to Customization.targets, you need to unload and reload your project in VS for those changes to be ‘picked up’ by VS.

Please let me know if you identify any flaws or improvements that may be made with this approach.

<Choose>
<When Condition=” ‘$(BuildEnvironment)’ == ‘DEV’ “>
<PropertyGroup>
<BaseUrlPmsServices>https://ecsport3.cps.com.au/Pms</BaseUrlPmsServices&gt;
<BaseUrlEcsServices>https://ecsport3.cps.com.au/Ecs</BaseUrlEcsServices&gt;
<BaseUrlEdasServices>https://edasport.cps.com.au:1800/services</BaseUrlEdasServices&gt;
<BaseUrlHelp>http://ecstest.cps.com.au/PmsHelp</BaseUrlHelp&gt;
<PublishDir>\\172.16.101.101\ReleaseMark\$(BuildEnvironment)\</PublishDir><!– InstallUrl>It seems that installUrl doesn’t get picked up if set here, must be fed in with the msbuild command arguments.</InstallUrl –>
<!– ApplicationVersion is set in the publishENV.bat –>
</PropertyGroup>
</When>
<When Condition=” ‘$(BuildEnvironment)’ == ‘SUB’ “>
<PropertyGroup>
<BaseUrlPmsServices>https://ecspmsport1/Pms</BaseUrlPmsServices&gt;
<BaseUrlEcsServices>https://ecspmsport1/Ecs</BaseUrlEcsServices&gt;
<BaseUrlEdasServices>https://edas2-port1:1800/services</BaseUrlEdasServices&gt;
<BaseUrlHelp>http://ecspmshq/PmsHelp</BaseUrlHelp&gt;
<PublishDir>\\ecspmsport1\SmartClient\</PublishDir>
</PropertyGroup>
</When>
<When Condition=” ‘$(BuildEnvironment)’ == ‘DPS’ “>
<PropertyGroup>
<BaseUrlPmsServices>https://ecspmsport2/Pms</BaseUrlPmsServices&gt;
<BaseUrlEcsServices>https://ecspmsport2/Ecs</BaseUrlEcsServices&gt;
<BaseUrlEdasServices>https://edas2-port2:1800/services</BaseUrlEdasServices&gt;
<BaseUrlHelp>http://ecspmshq/PmsHelp</BaseUrlHelp&gt;
<PublishDir>\\ecspmsport2\SmartClient\</PublishDir>
</PropertyGroup>
</When>
</Choose>