Jeremy Davis
Jeremy Davis
Sitecore, C# and web development
Article printed from: https://blog.jermdavis.dev/posts/2020/a-tool-to-help-you-build-config-patches

A tool to help you build config patches

Published 27 April 2020

Recently, I caught sight of a discussion in Sitecore Slack where the lack of tooling for helping you build config patch files came up. For some reason that struck a chord with me, and having mulled over it a bit, I decided I'd have a go at making something to see if it could be done...

Someone was taking about

Having spent a Sunday afternoon doing a bit of hacking, I tweeted an animation of my hacky code in action, and it seemed to strike a nerve with the community. Clearly there are a lot of people who fancy having some tooling for helping with patches...

So to keep you all happy, I've put some more hours into the code, and have posted my "PatchMaker" experiment on GitHub for you all to tear apart my hacky code enjoy.

The current release is available here: https://github.com/jermdavis/PatchMaker/releases

Grab the source if you want: https://github.com/jermdavis/PatchMaker.

And raise bugs if you find any issues.

Read on for info and explanations...

What can it do?

At this point the tool allows you to open an XML file, and then click nodes in the tree to create patching instructions to change the selected element or its surroundings. Key features include:

  • There's simple "find" behaviour to help you locate the XML nodes in the source tree that you want to change. Type some text in the search box and elements with matches in their name or attributes get highlighted.
  • It supports the common patching instructions from Sitecore config files: patch:instert, patch:instead, patch:delete, patch:attribute and set:attribute.
  • You can also add children to leaf-nodes in the xml tree. This doesn't use a patch instruction – it just adds the XML into the right place in the patch file, and Sitecore merges the new XML in.
  • It tries to validate your input, to prevent errors from bad XML.
  • You can generate and save XML patch files, which you can then deploy to your Sitecore instance.
  • You can view a preview of what happens to your source XML file when your patch is applied. This uses Sitecore's own patching engine, so should be accurate.
  • You can modify the patch instructions you're creating if you realise you change your mind.
  • You can also control the order that your patch instructions are applied in.
  • And it includes some basic built-in help about how to use it – click the help link in the menus, or on the title-bar of the key dialogs.

Limitations

It is an experiement so it's not perfect. The key known issue right now is that it doesn't really handle extra namespaces in your source XML very well. They show up in the tree view with the namespace URI in curly braces, and when it generates the defaults for new patch entries it will ignore any extra namespaces. Also, remember that while you can control the order of the patch instructions in the file you create, this code doesn't control the order that Sitecore applies patch files when your site starts up. So it's quite possible to create a patch here that works fine in the preview window, but fails when you drop it on your server...

It's built against .Net Framework 4.7.2 – so you'll need that installed to run it.

For licensing reasons the releases for this on GitHub can't include the Sitecore DLL necessary for previewing the effect of your patches. The app will run happily without that DLL – but the preview option will be disabled. (The button is greyed out, and there's a message in the status bar at the bottom of the main window) You'll need to add your own copy of Sitecore.Kernel.dll to the app's directory if you download it this way. (It's built against v8.2 because that had a NoReferences package and I was too lazy to work out how to achive that with a newer version. Other versions will probably work with a version redirect in the app.config) Alternatively, if you clone the source and build your own copy it will use NuGet to fetch this dll for you.

Plus, this is by no means the only way you can approach making patch files. Kamruz Jaman, who was part of the original discussion about patching UI that inspired me is working on his own approach as a web-based tool. And Mikael Högberg mentioned his approach in the twitter thread about all this. You might find you prefer that...

How does it work

The core of the logic here is in the PatchMaker.dll project. This defines a set of classes to represent each of the different types of patch. For example, a patch:instead operation is represented by the PatchMaker.PatchInstead class. These model classes are made up of some simple properties to represent the data needed for this patch:

public class PatchInstead : BasePatch
{
    public string XPathForParent { get; }
    public string XPathForReplacement { get; }
    public XElement Replacement { get; }
}

					

These have a constructor, which sets up these values, and they implement a method which can construct the correct XML for the patch based on the source XML document, and these properties:

public override void ApplyPatchElement(XDocument sourceXml, XDocument patchXml)
{
    var targetElement = sourceXml.SafeXPathSelectElement(XPathForParent);

    if (targetElement == null)
    {
        throw new PatchException(nameof(PatchInstead), XPathForParent, nameof(XPathForParent));
    }

    var newElement = new XElement(Replacement);
    newElement.Add(new XAttribute(Namespaces.Patch + "instead", XPathForReplacement));

    var currentPatchNode = base.CopyAncestorsAndSelf(targetElement, patchXml.Root);
    currentPatchNode.Add(newElement);
}

					

In general these methods are constructing the tree of ancestor elements, and then applying the right patch-namespace attributes and elements to match Sitecore's schema.

The PatchGenerator.cs class can take a source XML document and a set of these patch definitions, and combine them to give you the patch XML file. This is basically just generating the right root element, and then iterating each patch you defined to call ApplyPatchElement().

These classes were mostly built up using sort-of-TDD, so there are tests for them in the PatchMaker.Tests project. They're not exhaustive – but they've helped me catch a few bugs.

The UI for the tool is defined in the PatchMaker.App project. It's a basic Windows Forms app – mostly because I coudn't be bothered with the level of googling I would have needed to build it in WPF...

The main window is PatchPlanningForm.cs. This defines the XML tree view and the patch listing view that dominate the window, and wire up the assorted menus. When you click to load an XML file, it will parse a representation of the XML into the tree view. The TreeNodes have text for the element name and attributes, to give a visual reference of what you're working with. They also map the nodes' Tag property to the original XElement - as that raw data is used elsewhere.

Each of the patch types is managed by a window defined in the PatchForms folder. Both the attribute patches are managed by the same form, as their only difference is how the patch is rendered as XML. Insert, Delete and Instead (replace) get their own forms. Each of these shows you the context node and its ancestors as a visual reference. And it presents fields for filling in the properties of the models mentioned above. For example, the patch:insert window looks like:

Some fields are marked as required. Elements which take XML-text will validate the text, and present errors in a balloon tip. You can't accept the form when there are validation errors.

When you hit "ok" to close the form, an instance of the appropriate patch model is created and added to the on-screen list. You can double click the list to edit items, and right-click them do sort, edit and delete.

When you click the menu option to generate the patch file, the UI calls all the PatchGenerator class to process the patches you've defined, and displays the results for you:

Generate

This text is editable, if you want to add comments or tweak the results – but it's validated, so you can't have bad XML. You can click to save, or you can preview what happens when the patch is applied over the XML you started from. Since that uses Sitecore's own code to process the patch, it should be an accurate reflection of what your patch will do. (But note caveates above about patch file order, and the Sitecore DLL necessary for this code)

↑ Back to top