Трансформация XML- и JSON-конфигов при помощи Magic Chunks

Когда мы говорим о DevOps рано или поздно речь заходит о трансформации конфигурационных файлов. В мире .NET стандартом де-факто для конфигурационных файлов всегда был XML. А для трансформации XML-файлов был придуман XML Document Transform.

Появление .NET Core сместило многие акценты в мире .NET-разработки, включая работу с файлами конфигурации. XML, конечно же, поддерживается до сих пор. Однако, в качестве основного формата предлагается использовать JSON.

В .NET Core предполагается, что у вашего приложения есть несколько сред исполнения. Для каждого случая мы можем создать отдельный конфигурационный файл и положить туда нужные нам значения. Пусть, например, у нас есть файл appsettings.json и две среды исполнения, тогда конфигурационные файлы будут иметь следующие имена:

  • appsettings.Development.json − конфигурация для среды Development
  • appsettings.Production.json − конфигурация для среды Production

Такой подход решает задачу подмены значений в конфигурацинных файлах, но лично мне в этом не нравится как минимум два момента:

  1. Вы храните все конфигурационные файлы appsettings.json, appsettings.Development.json, appsettings.Production.json на каждом из серверов. Т.е. пароли и ключи для продакшн-ресурсов у вас будут также находится на серверах для тестирования.
  2. Иногда хочется вообще вынести все чувствительные данные (такие как пароли, ключи к базам данных, внешним ресурсам и т.д.) за пределы репозитория.

По этим причинам, трансформация конфигурационных файлов была бы предпочтительнее подмены файлов. И если с трансформацией XML всё понятно, то для JSON я таких инструментов не нашел. Поэтому тихим июньским субботним утром было решено изобретать свой велосипед на эту тему. О том, что из этого получилось — далее.

Magic Chunks

«Каким образом можно универсально представить трансформации» — именно этот вопрос встал в первую очередь. Чтобы не усложнять, в качестве рабочей идеи было принято представление трансформаций в виде последовательности «ключ − значение».

Допустим у вас есть следующий конфигурационный файл:

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5.1" />
    <authentication mode="None" />
  </system.web>
</configuration>

Чтобы изменить отдельные его части, мы указываем путь внутри файла и задаем для него значение. Разделителем является /. Например:

  • [email protected]false
  • [email protected]Forms

После трансформации файл конфигурации будет выглядеть так:

<configuration>
  <system.web>
    <compilation debug="false" targetFramework="4.5.1" />
    <authentication mode="Forms" />
  </system.web>
</configuration>

Приняв эту идею за основу, я сделал небольшую .NET-сборку, которая принимает на вход исходный файл и набор трансформаций, а на выходе выдает изменненый конфигурационный файл.

Такой подход позволяет трансформировать конфигурационные файлы независимо от того, к какому типу они относятся. На данный момент поддерживаются — XML, JSON и YAML. А для того, чтобы это можно было максимально удобно интегрировать в конкретный процесс сборки, было создано несколько разных оберток.

MSBuild

Чтобы запустить трансформацию конфигов внутри MSBuild-скрипта нужно загрузить последнюю версию Magic Chunks или установить её из Nuget.

После этого в ваш msbuild-файл следует внести несколько изменений:

Шаг 1: Импортировать MagicChunks.targets файл:

<Import Project="tools\MagicChunks.targets" />

Шаг 2: Определить коллекцию трансформаций. В MSBuild это можно сделать при помощи ItemGroup:

<ItemGroup>
  <TransformsXml Include="[email protected]">
    <Value>Forms</Value>
  </TransformsXml>
  <TransformsXml Include="configuration/system.web/httpRuntime">
    <Value>125</Value>
  </TransformsXml>
  ...
</ItemGroup>

Шаг 3: Запустить трансформацию:

<TransformConfig Path="Web.config" Trasformations="@(TransformsXml)" /> 

По умолчанию будет изменен исходный файл (в данном примере — Web.config). Если результат требуется сохранить в другом файле, то дополнительно следует задать атрибут Target:

<TransformConfig Path="Web.config" Target="App.config" Trasformations="@(TransformsXml)" /> 

Пример запуска трансформаций при помощи MSBuildsamples/msbuild

Powershell

Powershell — очень популярный инструмент для сборки проектов. Поэтому Magic Chunks имеет готовый модуль для Powershell. Потребуется загрузить последнюю версию Magic Chunks или установить её из Nuget.

После загрузки следует выполнить следующие шаги:

Шаг 1: Импортировать модуль MagicChunks.psm1:

Import-Module ".\MagicChunks.psm1";

Шаг 2: Запустить команду Format-MagicChunks с нужными параметрами:

Format-MagicChunks -path "$PSScriptRoot\Web.config" `
    -target "$PSScriptRoot\Web.transformed.1.config" -transformations @{
       "[email protected]" = "Forms";
       "configuration/system.web/httpRuntime" = "125";
}

Пример запуска трансформаций при помощи Powershellsamples/powershell

PSake

PSake — надстройка над Powershell, позволяющая писать билд-скрипты чуть более удобно. Поэтому запуск трансформаций производится так же, как в Powershell. Код, приведенный выше актуален и в данном случае.

Пример запуска трансформаций при помощи PSakesamples/psake

Cake

Cake (C# Make) — инструмент, позволяющий создавать билд-скрипты на языке C#. Чтобы подключить Magic Chunks к скрипту на Cake следует выполнить следующие шаги:

Шаг 1: Добавить ссылку на Magic Chunks:

#addin "MagicChunks"

Шаг 2: Запустить трансформацию, используя алиас TransformConfig:

Task("Default")
    .Does(() => {

        TransformConfig(@"Web.config", "Web.transformed.1.config",
          new TransformationCollection {
            { "[email protected]", "Forms" },
            { "configuration/system.web/httpRuntime", "125" }
          });

    });

Пример запуска трансформаций при помощи Cakesamples/cake

Запуск из собственного .NET-кода

Иногда может потребоваться произвести трансформацию из .NET-кода. Сделать это очень просто, т.к. Magic Chunks — это и есть сборка .NET:

Шаг 1: Устанавливаем Magic Chunks из Nuget:

Install-Package MagicChunks

Шаг 2: Создаем объект MagicChunks.Core.Transformer:

using MagicChunks.Core;

var transformer = new Transformer();

Шаг 3: Выполняем трансформацию:

string result = transformer.Transform(File.ReadAllText(@"Web.config"),
  new TransformationCollection
  {
    {"[email protected]", "Forms"},
    {"configuration/system.web/httpRuntime", "125"}
  });

Пример запуска трансформаций внутри .NET-проекта − samples/netapp

Плагин для TFS/Visual Studio Online

Для тех, кто используется Team Foundation Server или Visual Studio Online для сборки своего проекта, будет удобно использовать готовый плагин для трансформации. Сделать это весьма просто:

Шаг 1: Устанавливаем плагин из Visual Studio Marketplace.

Шаг 2: Добавляем новый шаг сборки:

Шаг 3: Выбираем Config transformation в группе Utility:

Шаг 4:" Указываем путь до файла, который нужно трансформировать, а также набор трансофрмаций в виде JSON-коллекции:

Шаг 5: Запускаем сборку.

Поскольку TFS на данный момент не имеет элементов управления для работы с коллекциями «ключ - значение», трансформации представляются в виде JSON-коллекции:

{
  "[email protected]": "false",
  "[email protected]": "Forms"
}

В качестве значений можно использовать переменные TFS:

{
  "configuration/appSettings/add[@key='builtBy'][email protected]": "$(Build.QueuedBy)"
}

Видео о том, как настроить трансформацию в TFS за 30 секунд: