В прошлой статье я рассказал как собрать CI часть пайплайна непрерывной поставки CI/CD на базе Azure DevOps Pipelines для Microsoft Dynamics CRM. Сегодня же поговорим о CD (Continuous Deployment) части.
Для релизов в Azure DevOps есть отдельные пайплайны (Release Pipelines) которые работают с результатами Build Pipeline, который мы делали в предыдущей статье, подробнее тут Release pipelines.
Сегодня будем развертывать наше приложение в CRM Online с помощью Power DevOps Tools. Это набор утилит по автоматизации развертывания CRM. Код доступен на GitHub https://github.com/WaelHamze/xrm-ci-framework. Чтобы его использовать его необходимо установить в свой Azure DevOps через Marketplace по аналогии с SonarCloud на которую я ссылался в предыдущей статье.
Пробную версию MS Dynamics CRM можно получить здесь https://trials.dynamics.com. Я создал две организации, Дев и Тест, с Дева буду забирать кастомизации чтобы потом вместе с всем остальным развернуть на Тесте.
Так как Power DevOps Tools немного по другому работает с плагинами, скриптами и солюшенами я сделал копию пайпа из прошлой статьи "CRMCI PDT" и немного его доработал. Старый переименовал в CRMCI SPKL.
Основные отличия от SPKL:
- описания шагов плагинов находятся в отдельном JSON-файле а не атрибутами;
- маппинг скриптов, также, находится в отдельном JSON-файле;
- Кастомизации не совместимы, solution XML распакованный SPKL не собирается PDT.
Это я и поправил в новом пайпе Build PDT.
Вот, что я добавил.
# Публикация веб-ресурсов и маппинга в хранилище артефактов
- task: CopyFiles@2
displayName: 'Copy webresources'
inputs:
SourceFolder: '$(Build.SourcesDirectory)\CRMCI\WebResources\'
Contents: |
gz_\**\*.js
gz_\**\*.css
gz_\**\*.html
TargetFolder: '$(Build.StagingDirectory)\webresources\'
CleanTargetFolder: true
- task: CopyFiles@2
displayName: 'Copy webresources mapping'
inputs:
SourceFolder: '$(Build.SourcesDirectory)\CRMCI\WebResources\'
Contents: 'WebResources.json'
targetFolder: '$(Build.StagingDirectory)\webresources\'
- publish: '$(Build.StagingDirectory)\webresources'
displayName: 'Publish webresources as an artifact'
artifact: webresources
# Установка Power DevOps Tools
- task: MSCRMToolInstaller@12
displayName: 'Install Power DevOps Tools'
inputs:
nugetFeed: 'official'
psFeed: 'official'
# Пакуем решение
- task: MSCRMPackSolution@12
inputs:
unpackedFilesFolder: '$(Build.SourcesDirectory)/CRMCI/Solution/Extracted'
mappingFile: '$(Build.SourcesDirectory)/CRMCI/Solution/SolutionMapping.xml'
outputPath: '$(build.binariesdirectory)/Solution'
# Публикация решения CRM
- task: CopyFiles@2
displayName: 'Publish CRM Solution to artefacts'
inputs:
SourceFolder: '$(build.binariesdirectory)\Solution\'
Contents: 'CICDDemo.zip'
TargetFolder: '$(Build.StagingDirectory)/solutions'
- publish: '$(Build.StagingDirectory)/solutions'
displayName: 'Publish solution as an artifact'
artifact: solutions
Также, я сделал еще один пайп, Export Solution, который выгружает решение и коммитит его в репозиторий. Идея в том, что пайп настроен на определенное решение и когда кто-то поработал над кастомизациями он запускает пайп чтобы зафиксировать внесенные изменения в репозитории. Ниже YAML этого пайпа:
trigger: none
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Debug'
crmmajor: 9
crmminor: 0
rel: 1
steps:
- task: MSCRMToolInstaller@12
displayName: 'Install Power DevOps Tools'
inputs:
nugetFeed: 'official'
psFeed: 'official'
# Выгружаем решение
- task: MSCRMExportSolution@12
displayName: 'Export CRM Solution'
inputs:
crmConnectionString: 'AuthType=OAuth;Url=https://orgc646f491.crm4.dynamics.com/;Username=$(UserName);Password=$(UserPwd);ClientId=$(ClientId);LoginPrompt=Never;RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97'
solutionName: 'CICDDemo'
exportManaged: false
exportUnmanaged: true
outputPath: '$(build.binariesdirectory)'
crmConnectionTimeout: '300'
useAsyncMode: true
- task: MSCRMExtractSolution@12
displayName: 'Unpack CRM Solution'
inputs:
unpackedFilesFolder: '$(Build.SourcesDirectory)/CRMCI/Solution/Extracted'
solutionFile: '$(build.binariesdirectory)/CICDDemo.zip'
- task: CmdLine@2
displayName: 'Commit solution to Git'
inputs:
script: >
echo set name
git config --local user.email "grigory@zhukoff.pro"
git config --local user.name "Grigory Zhukov"
git checkout $(Build.SourceBranchName)
echo pull
git pull https://Personal%20Access%20Token:$(PAT)@dev.azure.com/ZhukoffPublic/CRMCICD/_git/CRMCICD $(Build.SourceBranchName)
git add --all
git commit -m "Solution Update from Dev"
echo push code to $(Build.SourceBranchName)
git push https://Personal%20Access%20Token:$(PAT)@dev.azure.com/ZhukoffPublic/CRMCICD/_git/CRMCICD $(Build.SourceBranchName)
Для того чтобы подключиться к CRM Online необходимо сформировать строку подключения на базе OAuth2.0. Я использовал пример с сайта Microsoft Use connection strings in XRM tooling to connect to Microsoft Dataverse без последнего параметра TokenCacheStorePath и вместо AppId я использовал ClientId. :-)AuthType=OAuth;Username=jsmith@contoso.onmicrosoft.com;Password=passcode;Url=https://contosotest.crm.dynamics.com;AppId=51f81489-12ee-4a9e-aaae-a2591f45987d;RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97;TokenCacheStorePath=c:\MyTokenCache;LoginPrompt=Auto
Переходим к делу
Открываем наш проект, переходим в раздел Pipelines подраздел Releases. Там пока пусто так, что жмем кнопку New pipeline.
попадаем на страницу создания релизного пайпа. Как мы видим, в данном случае, дизайнер графический а сам пайп состоит из Artifacts и Stages, первое это то откуда мы берем наш билд а второе этапы пайпа которые состоят из джобов, которые, в свою очередь, состоят из тасков.
Также, нам сразу предлагается кучка готовых шаблонов различных джобов. К ним мы еще вернемся а сейчас жмем Empty job.
Теперь мы на основной странице редактирования пайпа. и здесь мы видим следующие секции:
- Основная панель работы с пайпом, в Artifacts мы настраиваем откуда и при каких условиях забирать билд а в Stages что дальше с ним делать и как.
- Основное меню настроек:
- Pipeline — основное окно работы с пайпом
- Tasks — переключение между тасками разных этапов
- Variables — настройка переменных для пайпа
- Retention — настройки времени жизни пайпа, через какое время экземпляр пайпа будет удален.
- Options — Настройки именования пайпа, его описание и настройки интеграций
- History — история изменений
- Дополнительное меню где можно сохранить текущий пайп, запустить, посмотреть предыдущие запуски и настроить безопасность.
- Окно настройки этапа, где можно поменять его имя, владельца и др. настройки безопасности.
Для начала нам нужно выбрать наш билд, который мы будем разворачивать. Для этого кликаем на Add an artifact и в поле Source (build pipeline) наш пайп Build PDT.
Если кликнуть по молнии в вернем правом углу то можно настроить триггеры для пайпа, например запускать при появлении нового билда. Также, можно настроить запуск по расписанию, кликнув на значок часов снизу.
Теперь переходим к нашему первому шагу Stage 1, кликаем на него и меняем название на Deploy Solutions. Здесь же, кликнув на молнию, можно настроить триггеры и фильтры для этапа а также, согласования и контроль качества (Quality gate).
Теперь настроим первый шаг развертывания на среду Тест. Для этого кликаем на 1 job, 0 task и попадаем на страницу настройки этапа, где кликаем на единственный Agent job.
Здесь можно поменять имя джобы, выбрать агента, в нашем случае это облачный агент "vs2017-win2016", и определить требования, например если нашему пайпу нужен PowerShell или Java.
Нажимаем + справа от Agent Job чтобы добавить первый таск и это будет Power DevopsTool Installer, т.к. мы используем облачного агента то надо установить все чего не хватает на виртуальной машине, где установлен агент. Если настраивать своего агента, то можно установить все необходимое и такие шаги пропускать.
В поле поиска вводим Power dev, находим Power DevopsTool Installer и жмем Add.
Затем, в поиске вводим import и добавляем таск Import Solution. После добавления мы увидим сообщение "Some settings need attention", кликаем на таск и видим что требуется ввести строку подключения.
Здесь, как и в билд-пайплайне, нам помогут переменные. Сохраняем пайп и переходим на закладку Variables 2 переменные, UserName, UserPwd и ClientId в которых сохраняем наш логин и пароль от облачной CRM соответственно.
Теперь возвращаемся к нашему шагу и вставляем в поле Connection string следующую строкуAuthType=OAuth;Url=https://org87d49aa0.crm4.dynamics.com/;Username=$(UserName);Password=$(UserPwd);ClientId=$(ClientId);LoginPrompt=Never;RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97
Не забудьте у себя поправить url организации.
Все, первый шаг готов.
Теперь для добавления следующего шага импорта плагинов переходим в главное окно кликнув на закладку Pipeline а затем на плюсик справа от Stages и на New stage.
Здесь снова выбираем Empty job дальше по аналогии с предыдущим этапом, только назовем его Deploy WebResources и вторым джобом будет Update Web resources? где надо будет указать строку подключения, тип развертывания, у меня с помощью файла маппинга .json, ссылку на папку с веб-ресурсами, ссылку на файл маппинга и наименование решения в которое публиковать указанные веб-ресурсы.
Файл маппинга выглядит следующим образом:
{
'webresources': [{
'root': '',
'files': [{
'uniquename': 'gz_/js/ContactFormEvents.js',
'file': 'js/ContactFormEvents.js',
'description': 'Скрипты формы Контакта'
}
]
}
]
}
И еще раз повторяем создание этапа для импорта плагинов, который назовем Plugin Registration и вторым джобом будет Plugin Registration. Так же указываем строку подключения, тип развертывания:
- Upsert: Обновляет существующие сборки/шаги и создает новые.
- Reset: Удаляет все существующие сборки/шаги и заново создает.
- Delsert: Делает тоже самое что и Upsert и потом удаляет сборки/шагикоторых нет в маппинге.
У меня Delsert, ссылку на папку с dll файлами, наименование решения в которое публиковать плагины и ссылку на файл маппинга.
В итоге мы получили пайп с тремя этапами которыйе идут последовательно друг за другом.
У многих может возникнуть вопрос почему я сделал три разных этапа по 2 джоба а не один этап с четырьмя джобами?
На это есть несколько причин:
- Если что-то пошло не так то можно перезапустить этап а джоб нельзя и если у вас какая-то проблема с плагинами не касающаяся самих плагинов, например, Deploy Admin нету у учетки от которой крутится агент а в сборке плагин не в сандбоксе. То, в случае одного этапа, придется перезапускатьего целиком и накатывать еще раз кастомизации и вебресурсы, что не очень удобно.
- Этапы можно запараллелить, что-бы они выполнялись одновременно. Например это можно сделать с плагинами и веб-ресурсами т.к. они зависят от кастомизаций но не зависят друг от друга. Для этого кликаем на Pre-deployment Conditions и, в появившемся меню у нас должно стоять Select trigger — After stage и в Stages мы ставим галку напротив Deploy Solution а напротив Deploy WebResources убираем.
to be continued...