ClickOnce, WPF, MSBuild и несколько окружений — I

Или сказ о том, как сделать публикацию приложений в один клик на разные окружения для тестирования разных версий приложений на WPF с помощью ClickOnce и TFS 2010.

На днях мне нужно было решить следующую, на мой взгляд, достаточно распространенную задачу, по размещению двух версий приложения. Одна версия QA – для тестирования текущих наработок, то, что реализуется каждый день: UI, логика, исправление мелких ошибок. Другая версия – Prod – для тестирования общих алгоритмов на реальных данных. В целом стандартная практика в мире разработки.

Решение этой задачи оказалось не столь стандартное и простое как я ожидал, для такой задачи. Но обо всем по порядку.

Диспозиция

В качестве исходных данных мы примем то, что у вас есть работоспособное приложение на WPF (для WinForm будет то же самое, но WPF более сложный случай), которое можно локально собрать в нескольких конфигурациях: Dev, QA(Cons), Prod.  Не важно, что именно у вас зависит от конфигурации, в общем случае, скорее всего это строки соединения с базой данных, оптимизация и логирование ошибок/действий пользователя.

Так же, как дополнительное условие, все конфигурации должны собираться на билд-сервере, в данном случае на TFS и при использовании MSBuild. То, что дальше описывается можно собирать локально, с помощью командной строки. Так что TFS – усложнение задачи, которое будем принимать во внимание.

И еще у вас успешно настроена на публикацию из VisualStudio с помощью ClickOnce хотя бы одна конфигурация приложения.

Еще раз, у нас есть:

  • WPF приложение
  • У приложения несколько рабочих конфигураций
  • Все конфигурации компилируются на билд-сервере (TFS)
  • Хотя бы одна конфигурация публикуется с помощью ClickOnce из VisualStudio

Проблема

В описанной системе все хорошо, все собирается на TFS, в разных конфигурациях. Т.е. я могу в любой момент получить рабочее приложение с любой конфигурацией. Однако распространение для пользователей идет только по линии QA. До определенного этапа разработки этого хватало. Теперь же надо опубликовать Prod, при этом на одном компьютере должны одновременно стоять как QA версия, так и Prod.
Проблем с физической публикацией не возникло. Т.е. на сервер складываются разные версии приложения, но при установке пользователю, одна версия затирает другую.

Кроме этой проблемы вам придется решить еще ряд других, не менее важных задач, как например обеспечение версионности приложения.

Надеюсь, что проблема ясна, но я более подробно покажу еще раз, как приходят к этой ситуации и как ее решить во всех подробностях.

Осознание проблемы

Все примеры будут на реальном приложении и окружении, так что исходных кодов не будет, так как они бессмыслены в текущем примере, но будет много картинок, по которым вы сможете быстро узнать свой случай и настроить все легко и безболезненно.

Итак, как было уже ранее сказано у вас есть приложение, которое работоспособно и собирается на билд-сервере.

Стандартная публикация ClickOnce

Публикация ClickOnce приложения настраивается в свойствах головного (исполняемого) проекта.

Вам надо настроить папку на сервере для выкладывания готового приложения, и задать адрес, где будет опубликован файл publish.htm

Опустим в данном случае Аpplication Files, Prerequisites, Updates, а остановимся на Options.

Здесь задаются основные данные, которые вы увидите на странице publish.htm, она, к слову, выглядит приблизительно так:

Здесь отображаются данные о названии продукта, компании, версии опубликованной. При этом прошу заметить, что версия публикации никак не коррелирует с версией приложения.

Для публикации версии Prod, придется менять адреса публикации, название продукта в меню Options, конфигурацию. Это все не очень удобно, и велика вероятность забыть поменять важные настройки.

Публиковать приложения из VS в целом является моветоном.

После того, как вы опубликовали приложения в две разные директории, это не значит, что они будут различными с точки зрения системы клиента. Они будут идентичны. Можете проверить на досуге.

Отличительные признаки

После копания в сети, оказалось, что отличительными признаками приложения с точки зрения клиентской системы являются:

  • Имя сборки
  • Имя приложения

Имя сборки:

Имя приложения:

Казалось бы, вот оно счастье и решение всех проблем, но не тут-то было.

Я уже упомянул, что для пущей сложности у нас WPF приложение, которое завязано на имя сборки, и поменять его запросто не получится, потому что у нас есть достаточное количество пользовательских экранов (UserControl), объявления XAML которых начинается как:

 

Не получается легкой жизни по всей видимости, т.е. если сменить названия продукта можно в зависимости от условий компиляции, то переписывать исходные коды… что-то мне не улыбается.

Дальнейший поиск и логика показали, что наверно можно поискать правды в файле %Публикуемый_проект%.application. Данный файл находится по адресу публикации приложения, и в директории сборки проекта.

В моем случае это Accounting.application. Это простой xml файл, который можно открыть в любом текстовом редакторе. Открываем и пытаемся найти там отличительные признаки.

Методом проб и ошибок, а так же с учетом ранее полученной информации обнаруживаем, что самая важная часть, для различения приложений является:

  • assemblyIdentity name=»Accounting.application» (строка 3)
  • asmv2:product=»ARM .Net (Test)» (строка 4)

Все остальное по большому счету не важно. Номер версии важен, но он не будет являться отличительным признаком приложения.

Т.е. если вы в одной из папок (QA) с опубликованным приложением поменяете эти значения, так чтобы они отличались от другой программы (Prod) и попробуете установить приложения, то вы получите 2 разных программы. Это выразится в двух ярлыках на рабочем столе и в меню пуск. Если конечно в настройках публикации указано на создание ярлыков.

Получается, что на данный момент мы выяснили что, как и где, надо поменять для того чтобы получить два разных приложения.

Хехе, большинство статей, чуть ли не на данном моменте останавливаются в повествовании, так как типа дальше дело техники поменять эти значения в процессе сборки приложения. Так вот посылаю лучи фертильности авторам таких постов.

Подготовка проекта

Прежде чем приступить к написанию скриптов по замене значений во время публикации приложений и всего такого прочего, необходимо должным образом подготовить проект и окружение к  будущим манипуляциям.

Первым делом надо задать значения папки для публикации, и адрес страницы публикации в зависимости от конфигурации проекта.

Для многих не новость, что настройки проекта меняются в зависимости от того, какая конфигурация выставлена. Например, в конфигурации Debug объявляются символы DEBUG, TRACE, можно указать определенные PreBuild и PostBuild действия. Если переключить конфигурацию в Release, то можно увидеть, как исчезнут галки с опций объявления символа DEBUG

Поэкспериментируйте с настройками, для кого это в новинку.

Однако настройки конфигурации не затрагивают закладку Publish c помощью, которой происходит публикация приложений, и значения:

  • Publishing Folder
  • Installation Folder URL

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

Настройка адресов публикации

По большому счету VisualStudio больше нам не помощник и все дальнейшие действия будут совершаться с помощью Notepad++ и командной строки из окружения VisualStudio.

Открываем в Notepad++ файл публикуемого проекта, в моем случае, как вы заметили это Accounting.csproj. Находим секции, где определяются настройки, зависящие от конфигурации. Их легко найти по отличительным признакам:

  • Находятся в начале файла, в районе 40-50 строки.
  • Начинаются с <PropertyGroup Condition=» ‘$(Configuration)|$(Platform)’ == ‘Debug|x86’ «>

В начале файла это практически всегда самые длинные строки. Итак, ищем свои конфигурации, в моем случае это:

  •   <PropertyGroup Condition=» ‘$(Configuration)|$(Platform)’ == ‘Release|x86’ «>
  •   <PropertyGroup Condition=»‘$(Configuration)|$(Platform)’ == ‘Prod|x86′»>

Первая для Cons, вторая для Prod. Вот так вот несогласовано, показываю на примере реального проекта, а создать конфигурацию легче и лучше, на мой взгляд, чем возиться с определениями символов.

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

 

Конечно, адреса сервера должны быть ваши. Не обращайте внимания, если VS будет ругаться на недопустимые тэги, все хорошо на самом деле. Полная секция в простейшем варианте выглядит так:

То же самое надо проделать для других конфигураций, которые должны идентифицироваться клиентом как разные программы. Вставить теги InstallUrl и PublishDir, указав значения папок и адресов для публикации приложения.

Данные теги являются переменными, которыми оперирует MSBuild при сборке проекта.

Проверка публикации

На данном этапе можно проверить работу только что введенных переменных путем публикации приложения из командной строки. Однако имя приложения все еще не изменится. Это мы сделаем немного позже и в другом файле. Сначала опробуем публикацию приложения из командной строки. Открываем Visual Studio Command Prompt

Переходим в директорию проекта, или же надо будет указать полный путь до файла .sln при использовани MSBuild.

Далее набираем  msbuild «arm .net.sln» /t:Publish /p:Configuration=Release и запускаем. Будет много-много текста, в конце концов напишут что ошибок нет и время публикации такое-то. В данном случае для нас важным параметром является указание цели сборки – Publish. Для этой цели происходит rebuild и публикация проекта. Проект будет опубликован с последней указанной версией на закладке Publish, и сколько бы раз вы не запускали эту команду.

Если все прошло без ошибок, то можно проверить, что опубликовалась версия для QA (удалите файлы из папки для публикации и запустите ее снова). В моем случае QA (Cons) так как она связана с конфигурацией Release.

Для публикации Prod версии надо будет выполнить msbuild «arm .net.sln» /t:Publish /p:Configuration=Prod

Если все хорошо, а так и должно быть, то переходим к следующему пункту. Тут стоит упомянуть, что при такой публикации не будет создана страница publish.htm, однако эту проблему мы решим, но чуть позже.

Версионность

Публикация с одной и той же версией нас не устраивает совершенно, так как тогда клиент не сможет распознать, что появилась новая версия. Версию можно передать во время сборки приложения с помощью параметра ApplicationVersion

msbuild «arm .net.sln» /t:Publish /p:Configuration=Release;ApplicationVersion=0.5.0.34

Но это неудобно, как увеличивать номера при автоматической сборке?

Вообще вопрос составления версий не столь простой как может показаться. На первый взгляд кажется, что нумерация в духе 0.5.1.456 вполне хороша. Я тоже так подумал, нашел решение для увеличения номера ревизии (последний номер в версии продукта), однако это решение опиралось  на внешний файл, в который и записывалась текущая версия. Такой подход не подходил для Continuous Deployment, так как после сборки я не смогу зачекинить файл с версией обратно в TFS. Честно сказать даже если это и возможно, то сама мысль мне показалась порочной и я не стал искать в этом направлении.

Следующей идеей, на которой я и остановился, представление версии в виде некоторой комбинации временных значений. Например, мне понравился вариант:

(Главная версия).(Версия и Год).(Порядковый номер дня в году).(Час и минута)

Примеры:

  • 1.411.302.1001
  • 0.012.12.2322

Данную версионность мы и реализуем в программе. Реализовать свою комбинацию будет не очень сложно, честное слово.

Версию приложения надо будет передать и для сборки, чтобы можно было в свойствах исполняемого файла ее увидеть, и для публикации.

 

Продолжение следует

Оставить комментарий