derickbailey/Albacore — Issue #199: Add an XmlUpdate task for app and web.config files.
Author: AnthonyMastrean
Created: 2012-03-08 19:33:37+00:00
State: open

I often want to rewrite the app or web config file for different kinds of builds:

* change log file paths in production (log4net config)
* modify application settings for simulation/production

I've used the [MSBuild Extension Pack][mep] XmlUpdate task before and that looks like this

I tried a custom Rake task that might be useful as a starting point. I have several exe configurations in my output and several replacements per configuration. So, I wrote up a hash of `xpath-query => replacement-text`. And I replaced all of those using xpath queries on every document.

require 'nokogiri'

task :update_profile => :build do
replacements = {
"/configuration/applicationSettings/*/setting[@name='EnableErrorInjection']/value" => 'False',
"/configuration/applicationSettings/*/setting[@name='ContainerProfile']/value" => 'RealHardware',
"/configuration/applicationSettings/*/setting[@name='FullScreen']/value" => 'True',
}

Dir.glob(File.join(output_path, '*.exe.config')).each do |path|

# you must open the file for reading separately from writing
file = File.open(path)
doc = Nokogiri::XML(file)
file.close

replacements.each do |key,value|
doc.xpath(key).each { |el| el.content = value }
end

xfile = File.open(path, "w+")
xfile.write(doc)
xfile.close
end
end

I tried using [rexml][rex] first, but it treats elements and [attributes][rex-a] differently. For elements, you can set the `text` property. On attributes, you have to find the element that contains it and overwrite the attribute/value pair. [Nokogiri][nok] allowed me to set the `content` of any element or [attribute][nok-a] the same way. This affected xpath queries like this

"/configuration/log4net/appender[@name='ErrorFileAppender']/file/@value" => 'E:\logs\Errors.txt'

The structure of the task could be pretty simple. This should satisfy the kind of strict one replacement per file for people coming from msbuild or the many to many style that I needed

xmlupdate :xmlupdate do |xml|
xml.replacement = {
"/configuration/applicationSettings/*/setting[@name='EnableErrorInjection']/value" => 'False',
"/configuration/applicationSettings/*/setting[@name='ContainerProfile']/value" => 'RealHardware',
"/configuration/applicationSettings/*/setting[@name='FullScreen']/value" => 'True',
}
xml.files Dir.glob(File.join(output_path, '*.exe.config'))
end

[mep]: http://msbuildextensionpack.codeplex.com/
[rex]: http://ruby-doc.org/stdlib-1.9.3/libdoc/rexml/rdoc/REXML.html
[rex-a]: http://ruby-doc.org/stdlib-1.9.3/libdoc/rexml/rdoc/REXML/Attribute.html
[nok]: http://nokogiri.org/
[nok-a]: http://nokogiri.org/Nokogiri/XML/Attr.html