Настройка конвейера CI/CD для автоматизации сборок MSIX и их развертыванияSet up a CI/CD pipeline to automate your MSIX builds and deployments

С помощью Azure Pipelines можно создавать автоматизированные сборки для проекта MSIX.You can use Azure Pipelines to create automated builds for your MSIX project. В этой статье рассматривается, как это сделать в Azure DevOps.This article takes a look at how to do so in Azure DevOps. Мы также покажем, как выполнить эти задачи с помощью командной строки, чтобы взаимодействовать с другими системами сборки.We’ll also show you how to perform these tasks by using the command line so that you can integrate with any other build system.

Создание конвейера AzureCreate a new Azure Pipeline

Зарегистрируйтесь в Azure Pipelines, если вы этого еще не сделали.Begin by signing up for Azure Pipelines if you haven't done so already.

Затем создайте конвейер, который можно использовать для сборки исходного кода.Next, create a pipeline that you can use to build your source code. Учебник по созданию конвейера для создания репозитория GitHub см. в статье Create your first pipeline (Создание первого конвейера).For a tutorial about building a pipeline to build a GitHub repository, see Create your first pipeline. Предложение Azure Pipelines поддерживает типы репозиториев, перечисленные в этой статье.Azure Pipelines supports the repository types listed in this article.

Чтобы настроить фактический конвейер сборки, перейдите на портал Azure DevOps по адресу dev.azure.com/<organization> и создайте проект.To set up the actual build pipeline, you browse to the Azure DevOps portal at dev.azure.com/<organization> and create a new project. Если у вас нет учетной записи, ее можно создать бесплатно.If you don’t have an account, you can create one for free. После входа и создания проекта можно отправить исходный код в репозиторий GIT, который будет настроен по адресу https://<organization>@dev.azure.com/<организация>/<project>/_git/<project>, или использовать любой другой поставщик, например GitHub.Once you’ve signed in and created a project, you can either push the source code to the Git repository that’s set up for you at https://<organization>@dev.azure.com/<organization>/<project>/_git/<project>, or use any other provider, such as GitHub. Вы можете выбрать расположение для своего репозитория при создании конвейера на портале. Для этого нажмите кнопку Конвейеры, а затем щелкните элемент Создать конвейер.You’ll get to choose the location of your repository when you create a new pipeline in the portal by clicking first on the Pipelines button and then on New Pipeline.

На появившемся экране Настройка выберите вариант Существующий файл YAML Azure Pipelines и путь к зарегистрированному файлу YAML в репозитории.On the Configure screen that comes next, you should select the Existing Azure Pipelines YAML file option and select the path to the checked-in YAML file in your repository.

Добавление сертификата проекта в библиотеку защищенных файловAdd your project certificate to the Secure files library

Примечание

Не следует отправлять сертификаты в репозиторий, если это возможно. По умолчанию GIT игнорирует их.You should avoid submitting certificates to your repo if at all possible, and git ignores them by default. Для управления безопасной обработкой конфиденциальных файлов, таких как сертификаты, Azure DevOps поддерживает функцию защищенных файлов.To manage the safe handling of sensitive files like certificates, Azure DevOps supports the secure files feature.

Чтобы отправить сертификат для автоматизированной сборки, выполните следующие действия.To upload a certificate for your automated build:

  1. В Azure Pipelines разверните узел Конвейеры в области навигации и щелкните элемент Библиотека.In Azure Pipelines, expand Pipelines in the navigation pane and click Library.
  2. Перейдите на вкладку Защищенные файлы и щелкните + Secure file (+ Защищенный файл).Click the Secure files tab and then click + Secure file.
  3. Перейдите к файлу сертификата и нажмите кнопку ОК.Browse to the certificate file and click OK.
  4. После отправки сертификата выберите его и просмотрите его свойства.After you upload the certificate, select it to view its properties. В разделе Разрешения конвейера включите переключатель Авторизовать использование во всех конвейерах.Under Pipeline permissions, enable the Authorize for use in all pipelines toggle.
  5. Если у закрытого ключа в сертификате есть пароль, рекомендуется сохранить пароль в Azure Key Vault, а затем связать пароль с группой переменных.If the private key in the certificate has a password, we recommend that you store your password in Azure Key Vault and then link the password to a variable group. Для доступа к паролю из конвейера можно использовать переменную.You can use the variable to access the password from the pipeline. Обратите внимание, что пароль поддерживается только для закрытого ключа. Использование файла сертификата, защищенного паролем, сейчас не поддерживается.Note that a password is only supported for the private key; using a certificate file that is itself password-protected is not currently supported.

Примечание

Начиная с Visual Studio 2019, временный сертификат больше не создается в проектах MSIX.Starting in Visual Studio 2019, a temporary certificate is no longer generated in MSIX projects. Чтобы создать или экспортировать сертификаты, используйте командлеты PowerShell, описанные в этой статье.To create or export certificates, use the PowerShell cmdlets described in this article.

Настройка сборки в файле YAMLConfigure the Build in your YAML file

В таблице ниже перечислены различные аргументы MSBuild, которые можно определить для настройки конвейера сборки.The table below lists the different MSBuild arguments you can define to setup your build pipeline.

Аргумент MSBuildMSBuild argument ЗначениеValue ОписаниеDescription
AppxPackageDirAppxPackageDir $(Build.ArtifactStagingDirectory)\AppxPackages$(Build.ArtifactStagingDirectory)\AppxPackages Определяет папку для хранения созданных артефактов.Defines the folder to store the generated artifacts.
AppxBundlePlatformsAppxBundlePlatforms $(Build.BuildPlatform)$(Build.BuildPlatform) Позволяет указать платформы, в которые будет включен пакет.Enables you to define the platforms to include in the bundle.
AppxBundleAppxBundle ВсегдаAlways Создает объединенный пакет MSIXBUNDLE или APPXBUNDLE с файлами MSIX или APPX для указанной платформы.Creates an .msixbundle/.appxbundle with the .msix/.appx files for the platform specified.
UapAppxPackageBuildModeUapAppxPackageBuildMode StoreUploadStoreUpload Создает файл MSIXBUNDLE или APPXBUNDLE и папку _Test для загрузки неопубликованных приложений.Generates the .msixupload/.appxupload file and the _Test folder for sideloading.
UapAppxPackageBuildModeUapAppxPackageBuildMode CICI Создает только файл MSIXBUNDLE или APPXBUNDLE.Generates the .msixupload/.appxupload file only.
UapAppxPackageBuildModeUapAppxPackageBuildMode SideloadOnlySideloadOnly Создает папку _Test только для загрузки неопубликованных приложений.Generates the _Test folder for sideloading only.
AppxPackageSigningEnabledAppxPackageSigningEnabled верноtrue Включает подписывание пакетов.Enables package signing.
PackageCertificateThumbprintPackageCertificateThumbprint Отпечаток сертификатаCertificate Thumbprint Это значение должно соответствовать отпечатку в сертификате для подписи, или строка должна быть пустой.This value must match the thumbprint in the signing certificate, or be an empty string.
PackageCertificateKeyFilePackageCertificateKeyFile ПутьPath Путь к файлу сертификата.The path to the certificate to use. Это значение извлекается из метаданных защищенного файла.This is retrieved from the secure file metadata.
PackageCertificatePasswordPackageCertificatePassword ПарольPassword Пароль для закрытого ключа в сертификате.The password for the private key in the certificate. Рекомендуется сохранить пароль в Azure Key Vault и связать пароль с группой переменных.We recommend that you store your password in Azure Key Vault and link the password to variable group. Можно передать переменную в этот аргумент.You can pass the variable to this argument.

Прежде чем создавать проект упаковки так же, как в мастере Visual Studio (с помощью командной строки MSBuild), процесс сборки может назначить версию создаваемого пакета MSIX, изменив атрибут Version элемента Package в файле Package.appxmanifest.Before building the packaging project the same way the wizard in Visual Studio does using the MSBuild command line, the build process can version the MSIX package that’s being produced by editing the Version attribute of the Package element in the Package.appxmanifest file. В Azure Pipelines это можно сделать с помощью выражения, указав переменную счетчика, значение которой будет увеличиваться для каждой сборки, и скрипта PowerShell, который использует класс System.Xml.Linq.XDocument в .NET, чтобы изменить значения атрибута.In Azure Pipelines, this can be achieved by using an expression for setting a counter variable that gets incremented for every build, and a PowerShell script that uses the System.Xml.Linq.XDocument class in .NET to change the value of the attribute.

Пример файла YAML, определяющего конвейер сборки MSIXSample YAML File that defines the MSIX Build Pipeline

pool: 
  vmImage: windows-2019
  
variables:
  buildPlatform: 'x86'
  buildConfiguration: 'release'
  major: 1
  minor: 0
  build: 0
  revision: $[counter('rev', 0)]
  
steps:
 - powershell: |
     # Update appxmanifest. This must be done before the build.
     [xml]$manifest= get-content ".\Msix\Package.appxmanifest"
     $manifest.Package.Identity.Version = "$(major).$(minor).$(build).$(revision)"    
     $manifest.save("Msix/Package.appxmanifest")
  displayName: 'Version Package Manifest'
  
- task: MSBuild@1
  inputs:
    solution: Msix/Msix.wapproj
    platform: $(buildPlatform)
    configuration: $(buildConfiguration)
    msbuildArguments: '/p:OutputPath=NonPackagedApp
     /p:UapAppxPackageBuildMode=SideLoadOnly  /p:AppxBundle=Never /p:AppxPackageOutput=$(Build.ArtifactStagingDirectory)\MsixDesktopApp.msix /p:AppxPackageSigningEnabled=false'
  displayName: 'Package the App'
  
- task: DownloadSecureFile@1
  inputs:
    secureFile: 'certificate.pfx'
  displayName: 'Download Secure PFX File'
  
- script: '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86\signtool"
    sign /fd SHA256 /f $(Agent.TempDirectory)/certificate.pfx /p secret $(
    Build.ArtifactStagingDirectory)/MsixDesktopApp.msix'
  displayName: 'Sign MSIX Package'
  
- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact: drop'

Ниже приведена разбивка различных задач сборки, определенных в файле YAML.Below are breakdowns of the different Build tasks defined in the YAMl file:

Настройка свойств создания пакетаConfigure package generation properties

Определение ниже задает каталог компонентов сборки, платформу и указывает, следует ли выполнять сборку пакета.The definition below sets the directory of Build components, the platform and defines whether to build a bundle or not.

/p:AppxPackageDir="$(Build.ArtifactStagingDirectory)\AppxPackages\"
/p:UapAppxPackageBuildMode=SideLoadOnly
/p:AppxBundlePlatforms="$(Build.BuildPlatform)"
/p:AppxBundle=Never

Настройка подписывания пакетаConfigure package signing

Чтобы подписать пакет MSIX (или APPX), конвейеру необходимо получить сертификат для подписи.To sign the MSIX (or APPX) package the pipeline needs to retrieve the signing certificate. Для этого добавьте задачу DownloadSecureFile перед задачей VSBuild.To do this, add a DownloadSecureFile task prior to the VSBuild task. Это обеспечит доступ к сертификату для подписи с помощью signingCert.This will give you access to the signing certificate via signingCert.

- task: DownloadSecureFile@1
  name: signingCert
  displayName: 'Download CA certificate'
  inputs:
    secureFile: '[Your_Pfx].pfx'

Затем обновите задачу MSBuild, чтобы она ссылалась на сертификат для подписи:Next, update the MSBuild task to reference the signing certificate:

- task: MSBuild@1
  inputs:
    platform: 'x86'
    solution: '$(solution)'
    configuration: '$(buildConfiguration)'
    msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" 
                  /p:AppxPackageDir="$(appxPackageDir)" 
                  /p:AppxBundle=Never 
                  p:UapAppxPackageBuildMode=SideLoadOnly 
                  /p:AppxPackageSigningEnabled=true
                  /p:PackageCertificateThumbprint="" 
                  /p:PackageCertificateKeyFile="$(signingCert.secureFilePath)"'

Примечание

В качестве меры предосторожности для аргумента PackageCertificateThumbprint намеренно задана пустая строка.The PackageCertificateThumbprint argument is intentionally set to an empty string as a precaution. Если отпечаток задан в проекте, но не соответствует сертификату для подписи, сборка завершится ошибкой: Certificate does not match supplied signing thumbprint.If the thumbprint is set in the project but does not match the signing certificate, the build will fail with the error: Certificate does not match supplied signing thumbprint.

Проверка параметровReview parameters

Параметры, определенные с использованием синтаксиса $() — это переменные, заданные в определении сборки, которые меняются в других системах сборки.The parameters defined with the $() syntax are variables defined in the build definition, and will change in other build systems.

Все предопределенные переменные см. в статье Use predefined variables (Предварительно заданные переменные сборки).To view all predefined variables, see Predefined build variables.

Настройка задачи публикации артефактов сборкиConfigure the Publish Build Artifacts task

Конвейер MSIX по умолчанию не сохраняет созданные артефакты.The default MSIX pipeline does not save the generated artifacts. Чтобы добавить возможность публикации в определение YAML, добавьте следующие задачи.To add the publish capabilities to your YAML definition, add the following tasks.

- task: CopyFiles@2
  displayName: 'Copy Files to: $(build.artifactstagingdirectory)'
  inputs:
    SourceFolder: '$(system.defaultworkingdirectory)'
    Contents: '**\bin\$(BuildConfiguration)\**'
    TargetFolder: '$(build.artifactstagingdirectory)'

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact: drop'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'

Созданные артефакты можно просмотреть в параметре Артефакты на странице результатов сборки.You can see the generated artifacts in the Artifacts option of the build results page.

Файл Установщика приложений для распространения по сторонним каналамAppInstaller file for non-store distribution

Если вы распространяете приложение за пределами Магазина, для установки и обновления пакета можно воспользоваться файлом Установщика приложений.If you're distributing your application outside the Store you can take advantage of the AppInstaller file for your package install and updates

Файл APPINSTALLER будет искать обновленные файлы в репозитории \server\foo.An .appinstaller File That Will Look for Updated Files on \server\foo

<?xml version="1.0" encoding="utf-8"?>
<AppInstaller xmlns="http://schemas.microsoft.com/appx/appinstaller/2018"
              Version="1.0.0.0"
              Uri="\\server\foo\MsixDesktopApp.appinstaller">
  <MainPackage Name="MyCompany.MySampleApp"
               Publisher="CN=MyCompany, O=MyCompany, L=Stockholm, S=N/A, C=Sweden"
               Version="1.0.0.0"
               Uri="\\server\foo\MsixDesktopApp.msix"
               ProcessorArchitecture="x86"/>
  <UpdateSettings>
    <OnLaunch HoursBetweenUpdateChecks="0" />
  </UpdateSettings>
</AppInstaller>

Элемент UpdateSettings сообщает системе периодичность проверки наличия обновлений, а также о необходимости принудительного обновления пользователем.The UpdateSettings element is used to tell the system when to check for updates and whether to force the user to update. Полный справочник по схемам, в котором также содержится список поддерживаемых пространств имен для каждой версии Windows 10, можно найти в документах по адресу bit.ly/2TGWnCR.The full schema reference, including the supported namespaces for each version of Windows 10, can be found in the docs at bit.ly/2TGWnCR.

Если добавить файл APPINSTALLER в проект упаковки и задать для свойства "Действие пакета" значение "Содержимое", а для свойства "Копировать в выходной каталог" — "Копировать более позднюю версию", то можно добавить в файл YAML еще одну задачу PowerShell, которая обновляет атрибуты версии корневого элемента и элемента MainPackage и сохраняет обновленный файл в промежуточном каталоге:If you add the .appinstaller file to the packaging project and set its Package Action property to Content and the Copy to Output Directory property to Copy if newer, you can then add another PowerShell task to the YAML file that updates the Version attributes of the root and MainPackage elements and saves the updated file to the staging directory:

- powershell: |
  [Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq")
  $doc = [System.Xml.Linq.XDocument]::Load(
    "$(Build.SourcesDirectory)/Msix/Package.appinstaller")
  $version = "$(major).$(minor).$(build).$(revision)"
  $doc.Root.Attribute("Version").Value = $version;
  $xName =
    [System.Xml.Linq.XName]
      "{http://schemas.microsoft.com/appx/appinstaller/2018}MainPackage"
  $doc.Root.Element($xName).Attribute("Version").Value = $version;
  $doc.Save("$(Build.ArtifactStagingDirectory)/MsixDesktopApp.appinstaller")
displayName: 'Version App Installer File'

Затем необходимо распространить файл APPINSTALLER среди пользователей, чтобы затем они могли дважды щелкнуть его вместо файла MSIX для установки упакованного приложения.You’d then distribute the .appinstaller file to your end users and let them double-click on this one instead of the .msix file to install the packaged app.

Непрерывное развертываниеContinuous Deployment

Файл Установщика приложений является неоткомпилированным файлом XML, который можно изменить после сборки, если это необходимо.The app installer file itself is an uncompiled XML file that can be edited after the build, if required. Это упрощает использование при развертывании программного обеспечения в нескольких средах и при необходимости отделения конвейера сборки от процесса выпуска.This makes it easy to use when you deploy your software to multiple environments and when you want to separate the build pipeline from the release process.

Если вы создадите конвейер выпуска на портале Azure с помощью шаблона "Пустое задание" и используете недавно настроенный конвейер сборки в качестве источника артефакта, можно добавить задачу PowerShell на этапе выпуска, чтобы значения двух атрибутов кода URI в файле APPINSTALLER динамически изменялись, указывая расположение публикации приложения.If you create a release pipeline in the Azure Portal using the “Empty job” template and use the recently set up build pipeline as the source of the artifact to be deployed, you can then add the PowerShell task to the release stage in order to dynamically change the values of the two Uri attributes in the .appinstaller file to reflect the location to which the app is published.

Ниже приведена задача конвейера выпуска, которая изменяет коды URI в файле APPINSTALLER:A Release Pipeline Task That Modifies the Uris in the .appinstaller File

- powershell: |
  [Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq")
  $fileShare = "\\filesharestorageccount.file.core.windows.net\myfileshare\"
  $localFilePath =
    "$(System.DefaultWorkingDirectory)\_MsixDesktopApp\drop\MsixDesktopApp.appinstaller"
  $doc = [System.Xml.Linq.XDocument]::Load("$localFilePath")
  $doc.Root.Attribute("Uri").Value = [string]::Format('{0}{1}', $fileShare,
    'MsixDesktopApp.appinstaller')
  $xName =
    [System.Xml.Linq.XName]"{http://schemas.microsoft.com/appx/appinstaller/2018}MainPackage"
  $doc.Root.Element($xName).Attribute("Uri").Value = [string]::Format('{0}{1}',
    $fileShare, 'MsixDesktopApp.appx')
  $doc.Save("$localFilePath")
displayName: 'Modify URIs in App Installer File'

В задаче выше в качестве кода URI указан UNC-путь к общей папке Azure.In the task above, the URI is set to the UNC path of an Azure file share. В этом случае операционная система будет искать пакет MSIX при установке и обновлении приложения, поэтому в конвейер выпуска также добавлен еще один скрипт командной строки, который сначала сопоставляет общую папку в облаке с локальным диском Z:\ в агенте сборки, а затем использует команду xcopy, чтобы копировать на этот диск файлы APPINSTALLER и MSIX:Because this is where the OS will look for the MSIX package when you install and update the app, I’ve also added another command-line script to the release pipeline that first maps the file share in the cloud to the local Z:\ drive on the build agent before it uses the xcopy command to copy the .appinstaller and .msix files there:

- script: |
  net use Z: \\filesharestorageccount.file.core.windows.net\myfileshare
    /u:AZURE\filesharestorageccount
    3PTYC+ociHIwNgCnyg7zsWoKBxRmkEc4Aew4FMzbpUl/
    dydo/3HVnl71XPe0uWxQcLddEUuq0fN8Ltcpc0LYeg==
  xcopy $(System.DefaultWorkingDirectory)\_MsixDesktopApp\drop Z:\ /Y
  displayName: 'Publish App Installer File and MSIX package'

Если у вас есть собственный локальный сервер Azure DevOps Server, можно опубликовать файлы во внутренней сетевой папке.If you host your own on-premises Azure DevOps Server, you may of course publish the files to your own internal network share.

В случае публикации на веб-сервере с помощью MSBuild можно создать файл APPINSTALLER с контролем версий и страницу HTML, содержащую ссылку для скачивания, а также некоторые сведения об упакованном приложении, указав несколько дополнительных аргументов в файле YAML:If you choose to publish to a Web server, you can tell MSBuild to generate a versioned .appinstaller file and an HTML page that contains a download link and some information about the packaged app by supplying a few additional arguments in the YAML file:

- task: MSBuild@1
  inputs:
    solution: Msix/Msix.wapproj
    platform: $(buildPlatform)
    configuration: $(buildConfiguration)
    msbuildArguments: '/p:OutputPath=NonPackagedApp /p:UapAppxPackageBuildMode=SideLoadOnly  /p:AppxBundle=Never /p:GenerateAppInstallerFile=True
/p:AppInstallerUri=http://yourwebsite.com/packages/ /p:AppInstallerCheckForUpdateFrequency=OnApplicationRun /p:AppInstallerUpdateFrequency=1 /p:AppxPackageDir=$(Build.ArtifactStagingDirectory)/'
  displayName: 'Package the App'

Созданный HTML-файл будет содержать гиперссылку с префиксом схемы активации протокола ms-appinstaller, независящей от браузера:The generated HTML file includes a hyperlink prefixed with the browser-agnostic ms-appinstaller protocol activation scheme:

<a href="ms-appinstaller:?source=
  http://yourwebsite.com/packages/Msix_x86.appinstaller ">Install App</a>

Если вы настроили конвейер выпуска, который публикует содержимое папки сброса в интрасети или на любом другом веб-сайте, а веб-сервер поддерживает запросы байтового диапазона и правильно настроен, пользователи смогут использовать эту ссылку для прямой установки приложения без предварительного скачивания пакета MSIX.If you set up a release pipeline that publishes the contents of the drop folder to your intranet or any other Web site, and the Web server supports byte-range requests and is configured properly, your end users can use this link to directly install the app without downloading the MSIX package first.