After the main program is deployed the maintenance will have to deploy fixpacks and service packs. I wondered how I should handle this with burn and decided to use a separate patch bundle.
Versioning used in this sample is MajorNumber.ServicePack.FixPack. Each patch bundle also has a separate UpgradeCode, because when removing bundle v1.0.0.1 while bundle v1.0.0.2 was installed might leave the system with the v1.0.0.1 patch (issues about superseded patches.. see here).
Now I had several bundles, which all were authored to be a patch for the main bundle. That looks like this:
<Bundle Name="PatchingBundle 1.0.0.1" Version="1.0.0.1" Manufacturer="Jakob Devs Inc." UpgradeCode="[patch_bundle_code_v1.0.0.1]" ParentName="BaselineBootstrapper"> <RelatedBundle Id="[parent_bundle_code]" Action="Patch" /> </Bundle>
There are several things of note here:
- The UpgradeCode of this patch bundle is new, this leads to a separate entry in ARP's updates
- ParentName: this puts the bundle in ARP's update section under the name BaselineBootstrapper. This should be the same for all patches.
- RelatedBundle element: here I specify that this bundle patches the <parent_bundle>. This leads to this bundle being uninstalled when the <parent_bundle> is uninstalled. Cool :)
To remove a Sp1Fp1 patch when the service pack is uninstalled, the parent_bundle_code will be the UpgradeCode of the ServicePack patch bundle.
Once a service pack is deployed, there'll be patches which target the service pack. These msp patches won't install on systems which don't have the service pack installed. I talked about that topic here.
To get a state on the system where the user can remove installed patches, the burn engine offered me basically one solution:
Install every patch under a different UpgradeCode, so that all patch bundles show up in ARP.
Install every patch under a different UpgradeCode, so that all patch bundles show up in ARP.
The patch itself could theoretically be installed as superseding or not, I didn't find any difference so far. However, burn does not remove superseded patches, that's why I opted for non-superseding patches. They still all contain the diff to the baseline, though. Baselines will be the RTM or SP releases.
[Edit]: Apparently, an upgrade of version 1.0.0.x to 1.0.1 (yep, a service pack) always makes the patch superseding. That also means, when I'd uninstall bundle 1.0.0.1 while 1.0.1 is installed, the superseded patch stays on the system.
Just for the sake of completeness: what would the complete sequence of main installer, service pack, fix pack for SP look like?
[Edit]: Apparently, an upgrade of version 1.0.0.x to 1.0.1 (yep, a service pack) always makes the patch superseding. That also means, when I'd uninstall bundle 1.0.0.1 while 1.0.1 is installed, the superseded patch stays on the system.
Just for the sake of completeness: what would the complete sequence of main installer, service pack, fix pack for SP look like?
<Bundle Name="Main RTM Bundle 1.0.0.0" Version="1.0.0.0" Manufacturer="Jakob Devs Inc." UpgradeCode="[main_bundle_code]" > <!-- no related bundle here ... --> </Bundle>
The service pack is basically again a patch, so we'll relate it to the main RTM bundle above.
<Bundle Name="PatchingBundle 1.0.1.0 - SP1" Version="1.1.0" Manufacturer="Jakob Devs Inc." UpgradeCode="[patch_bundle_code_v1.0.1.0]" ParentName="BaselineBootstrapper"> <RelatedBundle Id="[main_bundle_code]" Action="Patch" /> </Bundle>
Then I would author the fix pack for this service pack as this, but I relate it to the service pack patch. Of course this fix pack should have the diff from the SP1 baseline to the patch. It doesn't have to use the RTM as the baseline anymore. It'll get uninstalled when the SP is uninstalled:
<project name="integrator" basedir="."> <property name="baseDir" value="${project::get-base-directory()}" /> <target name="build"> <call target="cleanBuildfiles" /> <call target="buildSoftware" /> <call target="buildInstaller" /> <call target="publishSoftware" /> </target> <target name="buildSoftware"> <nant buildfile="${baseDir}\myFramework\default.build" target="buildAll" inheritall="false"/> </target> <target name="buildInstaller"> <nant buildfile="${baseDir}\installer\default.build" target="buildInstaller" inheritall="false" /> </target> </project>
Here it also makes sense to use the <bal:Condition> element so that the Sp1Fp1 only installs if the service pack for the main RTM bundle is installed.