2008/04/21

Delay signing Click One application

Along the lines of build one; deploy many, I have been working on setting up a ClickOnce application that would be compiled and published once. This published set of files would then be configured for particular environment, signed, and set for public consumption. The problem is that when a ClickOnce application is published, the application and deployment manifests get signed and any changes to the files mean that the checksum values no longer line up. So how do you publish once and then make changes without breaking the newly signed manifests? Below is a subset of the proj file we are using. The csproj is called with OutputPath=$(DropLocation)\Setup\Raw\ and Targets=PublishOnly


<?xml version="1.0" encoding="utf-8" ?><project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" defaulttargets="Publish">
<import project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets">
<propertygroup>
<droplocation condition="'$(DropLocation)'==''">C:\VSDumpingGround\OurApp</droplocation>
<magepath condition="'$(MagePath)'==''">C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\mage.exe</magepath>
<certfile condition="'$(CertFile)'==''">$(DropLocation)\OurApp.Key.pfx</certfile>
<signingcertpassword condition="'$(SigningCertPassword)'==''">password</signingcertpassword>
<publishername condition="'$(PublisherName)'==''">Our Company</publishername>
<buildnumberfile>C:\VSDumpingGround\BuildNumber.txt</buildnumberfile>
</propertygroup>
<propertygroup>
<publishdependson>
MageOurApp;
RemoveRawFiles
</publishdependson>
</propertygroup>
<target name="Publish" dependsontargets="$(PublishDependsOn)">
</target>
<target name="VersionOurApp">
<!-- MSBuild Community Task get and increment the versin number stored in text file-->
<version revisiontype="Increment" versionfile="$(BuildNumberFile)">
<output propertyname="Major" taskparameter="Major">
<output propertyname="Minor" taskparameter="Minor">
<output propertyname="Build" taskparameter="Build">
<output propertyname="Revision" taskparameter="Revision">
</version>
<version major="$(Major)" minor="$(Minor)" build="$(Build)" revision="$(Revision)">
</version>
</target>
<target name="MoveApplicationFiles" dependsontargets="VersionOurApp">
<createproperty value="$(DropLocation)\Setup\OurApp.application">
<output propertyname="ApplicationFile" taskparameter="Value">
</createproperty>
<!-- Move all files in the app.publish directory up to the setup folder-->
<createitem include="$(DropLocation)\Setup\Raw\app.publish\*" exclude="$(ApplicationFile)">
<output itemname="MoveSetup" taskparameter="Include">
</createitem>
<Copy SourceFiles="@(MoveSetup)"
DestinationFolder="$(DropLocation)\Setup\"/>
<!-- Remove any duplicate files from the list-->
<createitem include="$(DropLocation)\Setup\Raw\app.publish\Application Files\OurApp*\*">
<output itemname="AppFilesRoot" taskparameter="Include">
</createitem>
<removeduplicates inputs=""> '%(rootdir)%(directory)')">
<output itemname="FilteredAppFilesRoot" taskparameter="Filtered">
</removeduplicates>
<createitem include="%(FilteredAppFilesRoot.Identity)**\*">
<output itemname="MoveAppFiles" taskparameter="Include">
</createitem>
<!-- Copy the deploy files to the Application Files\OurApp_X_X_X_X directory -->
<Copy SourceFiles="@(MoveAppFiles)"
DestinationFolder="$(DropLocation)\Setup\Application Files\OurApp_$(Major)_$(Minor)_$(Build)_$(Revision)\%(RecursiveDir)"/>
<removedir directories="$(DropLocation)\Setup\Raw\">
</target>
<propertygroup>
<prepourappdependson>
MoveApplicationFiles;
VersionOurApp;
ConfigureOurApp
</prepourappdependson>
</propertygroup>
<target name="PrepOurApp" dependsontargets="$(PrepOurAppDependsOn)">
<createproperty value="$(DropLocation)\Setup\Application Files\OurApp_$(Major)_$(Minor)_$(Build)_$(Revision)">
<output taskparameter="Value" propertyname="ApplicationDirectory">
</createproperty>
<createproperty value="$(DropLocation)\Setup\Application Files\OurApp_$(Major)_$(Minor)_$(Build)_$(Revision)\OurApp.exe.manifest">
<output propertyname="ManifestFile" taskparameter="Value">
</createproperty>
<createproperty value="$(DropLocation)\Setup\Raw">
<output propertyname="RawDirectory" taskparameter="Value">
</createproperty>
<!-- Remove the manifest file if it exists-->
<delete files="$(ManifestFile)" continueonerror="true">
<!-- copy files to a new Raw directory-->
<CreateItem Include="$(ApplicationDirectory)\**\*"
Exclude="$(ManifestFile)">
<Output ItemName="DeployFiles"
TaskParameter="Include"/>
</createitem>
<!-- This removes the .deploy from the files as you copy them to a new Raw directory-->
<Copy SourceFiles="@(DeployFiles)"
DestinationFiles="@(DeployFiles -> '$(DropLocation)\Setup\Raw\%(RecursiveDir)\%(FileName)')"/>
</target>
<propertygroup>
<configureourappdependson>
VersionOurApp;
MoveApplicationFiles
</configureourappdependson>
</propertygroup>
<target name="ConfigureOurApp" dependsontargets="$(ConfigureOurAppDependsOn)">
<!-- See my blog about editing XML config files
You would use the files in the newly created $(DropLocation)\Setup\Raw\... location(s)-->
</target>
<propertygroup>
<mageourappdependson>
ConfigureOurApp;
VersionOurApp;
PrepOurApp
</mageourappdependson>
</propertygroup>
<target name="MageOurApp" dependsontargets="$(MageOurAppDependsOn)">
<!-- Generate new application manifest-->
<GenerateApplicationManifest
AssemblyName="OurApp.exe"
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
EntryPoint="$(RawDirectory)\OurApp.exe"
OutputManifest="$(ManifestFile)"/>
<!-- Sign the application manifest-->
<!-- %22 takes the place of "-->
<!-- This signs the newly created manifest-->
<exec command="%22$(MagePath)%22 -Update %22$(ManifestFile)%22 -fd %22$(RawDirectory)%22 -cf %22$(CertFile)%22 -pwd $(SigningCertPassword)">
<!-- Generate new deployment manifest-->
<GenerateDeploymentManifest
AssemblyName="OurApp.application"
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
DeploymentUrl="http://somecompany.com/setup/OurApp.application"
DisallowUrlActivation="false"
EntryPoint="$(ManifestFile)"
Install="true"
MapFileExtensions="true"
MinimumRequiredVersion ="$(Major).$(Minor).$(Build).$(Revision)"
OutputManifest="$(ApplicationFile)"
Product="Our Product"
Publisher="$(PublisherName)"
UpdateEnabled="true"
UpdateMode="Foreground">
</generatedeploymentmanifest>
<!-- Sign the deployment manifest-->
<!-- %22 takes the place of "-->
<!-- We had issues with Publisher and Product not properly propogating to the .application file. We are on framework 3.0 so we can't use UseApplicationTrust, Publisher, Product on the GenerateApplicationManifest task-->
<!-- This signs the newly created manifest and forces in Publisher/Product. Marks the deployment manifest as used for trust-->
<exec command="%22$(MagePath)%22 -Update %22$(ApplicationFile)%22 -cf %22$(CertFile)%22 -providerurl %22http://somecompany.com/setup/OurApp.application%22 -Tofile %22$(ApplicationFile)%22 -appm %22$(ManifestFile)%22 -pwd $(SigningCertPassword) -pub %22$(PublisherName)%22 -UseManifestForTrust true">
</target>
<target name="RemoveRawFiles" dependsontargets="MageOurApp">
<removedir directories="$(RawDirectory)">
</target>
</project>

No comments:

Comments