Configure um pipeline da CI/CD para automatizar builds e implantações do MSIXSet up a CI/CD pipeline to automate your MSIX builds and deployments

Você pode usar o Azure Pipelines para criar builds automatizados para seu projeto MSIX.You can use Azure Pipelines to create automated builds for your MSIX project. Este artigo mostra como fazer isso no Azure DevOps.This article takes a look at how to do so in Azure DevOps. Também mostraremos como realizar essas tarefas usando a linha de comando para que seja possível integrar com qualquer outro sistema de build.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.

Crie um novo pipeline do AzureCreate a new Azure Pipeline

Comece inscrevendo-se no Azure Pipelines caso ainda não tenha feito isso.Begin by signing up for Azure Pipelines if you haven't done so already.

Em seguida, crie um pipeline que possa ser usado para criar seu código-fonte.Next, create a pipeline that you can use to build your source code. Para obter um tutorial sobre como criar um pipeline para criar um repositório GitHub, consulte Crie seu primeiro pipeline.For a tutorial about building a pipeline to build a GitHub repository, see Create your first pipeline. O Azure Pipelines é compatível com os tipos de repositório listados neste artigo.Azure Pipelines supports the repository types listed in this article.

Para configurar o pipeline do build propriamente dito, navegue para o portal Azure DevOps em dev.azure.com/<organization> e crie um novo projeto.To set up the actual build pipeline, you browse to the Azure DevOps portal at dev.azure.com/<organization> and create a new project. Se não tiver uma conta, crie uma gratuitamente.If you don’t have an account, you can create one for free. Depois de entrar e criar um projeto, você poderá enviar o código-fonte por push ao repositório Git configurado para você em https://<organization>@dev.azure.com/<organization>/<project>/_git/<project> ou usar qualquer outro provedor, como o 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. Você poderá escolher a localização do repositório ao criar um pipeline no portal, basta primeiro clicar no botão Pipelines, depois em Novo Pipeline.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.

Na tela Configurar que aparecerá em seguida, você deverá selecionar a opção Arquivo YAML existente do Azure Pipelines e o caminho para o arquivo YAML verificado no repositório.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.

Adicione o certificado do projeto à biblioteca Arquivos segurosAdd your project certificate to the Secure files library

Observação

Se possível, você deve evitar enviar certificados para o seu repositório e o git os ignorará por padrão.You should avoid submitting certificates to your repo if at all possible, and git ignores them by default. Para gerenciar o tratamento seguro de arquivos confidenciais como certificados, o Azure DevOps oferece suporte ao recurso arquivos seguros.To manage the safe handling of sensitive files like certificates, Azure DevOps supports the secure files feature.

Para carregar um certificado em seu build automatizado:To upload a certificate for your automated build:

  1. No Azure Pipelines, expanda Pipelines no painel de navegação e clique em Biblioteca.In Azure Pipelines, expand Pipelines in the navigation pane and click Library.
  2. Clique na guia Arquivos seguros e clique em + Arquivo seguro.Click the Secure files tab and then click + Secure file.
  3. Navegue até o arquivo de certificado e clique em OK.Browse to the certificate file and click OK.
  4. Depois de carregar o certificado, selecione-o para visualizar as propriedades dele.After you upload the certificate, select it to view its properties. Em Permissões de pipeline, habilite a opção Autorizar para uso em todos os pipelines.Under Pipeline permissions, enable the Authorize for use in all pipelines toggle.
  5. Se a chave privada no certificado tiver uma senha, recomendamos que você a armazene no Azure Key Vault e a vincule a um grupo de variáveis.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. Você pode usar a variável para acessar a senha pelo pipeline.You can use the variable to access the password from the pipeline. Observe que uma senha é compatível apenas com a chave privada. No momento, não é possível usar um arquivo de certificado que seja, em si, protegido por senha.Note that a password is only supported for the private key; using a certificate file that is itself password-protected is not currently supported.

Observação

A partir do Visual Studio 2019, um certificado temporário não é mais gerado nos projetos MSIX.Starting in Visual Studio 2019, a temporary certificate is no longer generated in MSIX projects. Para criar ou exportar certificados, use os cmdlets do PowerShell descritos neste artigo.To create or export certificates, use the PowerShell cmdlets described in this article.

Configure o Build no arquivo YAMLConfigure the Build in your YAML file

Os diferentes argumentos MSBuild que podem ser definidos para configurar o pipeline do build estão listados na tabela a seguir.The table below lists the different MSBuild arguments you can define to setup your build pipeline.

Argumento MSBuildMSBuild argument ValorValue DescriçãoDescription
AppxPackageDirAppxPackageDir $(Build.ArtifactStagingDirectory)\AppxPackages$(Build.ArtifactStagingDirectory)\AppxPackages Define a pasta para armazenar os artefatos gerados.Defines the folder to store the generated artifacts.
AppxBundlePlatformsAppxBundlePlatforms $(Build.BuildPlatform)$(Build.BuildPlatform) Permite definir quais plataformas incluir no pacote.Enables you to define the platforms to include in the bundle.
AppxBundleAppxBundle SempreAlways Cria um .msixbundle/.appxbundle com os arquivos .msix/.appx da plataforma especificada.Creates an .msixbundle/.appxbundle with the .msix/.appx files for the platform specified.
UapAppxPackageBuildModeUapAppxPackageBuildMode StoreUploadStoreUpload Gera o arquivo .msixupload/.appxupload e a pasta _Test para sideload.Generates the .msixupload/.appxupload file and the _Test folder for sideloading.
UapAppxPackageBuildModeUapAppxPackageBuildMode CICI Gera somente o arquivo .msixupload/.appxupload.Generates the .msixupload/.appxupload file only.
UapAppxPackageBuildModeUapAppxPackageBuildMode SideloadOnlySideloadOnly Gera a pasta _Test apenas para sideload.Generates the _Test folder for sideloading only.
AppxPackageSigningEnabledAppxPackageSigningEnabled verdadeirotrue Habilita a assinatura do pacote.Enables package signing.
PackageCertificateThumbprintPackageCertificateThumbprint Impressão digital do certificadoCertificate Thumbprint Esse valor deve corresponder à impressão digital no certificado de autenticação ou ser uma cadeia de caracteres vazia.This value must match the thumbprint in the signing certificate, or be an empty string.
PackageCertificateKeyFilePackageCertificateKeyFile CaminhoPath O caminho para o certificado a ser usado.The path to the certificate to use. Isso é recuperado pelos metadados do arquivo seguro.This is retrieved from the secure file metadata.
PackageCertificatePasswordPackageCertificatePassword SenhaPassword A senha da chave privada no certificado.The password for the private key in the certificate. Recomendamos que você armazene a senha no Azure Key Vault e vincule-a ao grupo de variáveis.We recommend that you store your password in Azure Key Vault and link the password to variable group. Você pode passar a variável para este argumento.You can pass the variable to this argument.

Antes de criar o projeto de empacotamento da mesma forma que o assistente no Visual Studio usando a linha de comando MSBuild, o processo de build pode criar a versão do pacote MSIX sendo produzido editando o atributo Version no elemento Package no arquivo 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. No Azure Pipelines, isso pode ser feito usando-se uma expressão para configurar uma variável de contador que é incrementada para cada build e um script do PowerShell que usa a classe System.Xml.Linq.XDocument no .NET para alterar o valor do atributo.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.

Exemplo de arquivo YAML que define o Pipeline de Build do 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'

Abaixo está o detalhamento das diferentes tarefas de Build definidas no arquivo YAMl:Below are breakdowns of the different Build tasks defined in the YAMl file:

Configure propriedades de geração de pacotesConfigure package generation properties

A definição abaixo estabelece o diretório dos componentes do Build, a plataforma e se um pacote será criado.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 a autenticação do pacoteConfigure package signing

Para autenticar o pacote MSIX (ou APPX), o pipeline precisa recuperar o certificado de autenticação.To sign the MSIX (or APPX) package the pipeline needs to retrieve the signing certificate. Para isso, adicione uma tarefa DownloadSecureFile antes da tarefa VSBuild.To do this, add a DownloadSecureFile task prior to the VSBuild task. Isso possibilitará o acesso ao certificado de autenticação por 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'

Em seguida, atualize a tarefa MSBuild para fazer referência ao certificado de autenticação: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)"'

Observação

O argumento PackageCertificateThumbprint é definido intencionalmente como uma cadeia de caracteres vazia por precaução.The PackageCertificateThumbprint argument is intentionally set to an empty string as a precaution. Caso a impressão digital esteja definida no projeto, mas não corresponda ao certificado de autenticação, o build falhará com o erro: 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.

Revise os parâmetrosReview parameters

Os parâmetros definidos com a sintaxe $() são variáveis ajustadas na definição do build e serão alterados em outros sistemas de build.The parameters defined with the $() syntax are variables defined in the build definition, and will change in other build systems.

Para ver todas as variáveis predefinidas, consulte Variáveis de build predefinidas.To view all predefined variables, see Predefined build variables.

Configure a tarefa Publicar Artefatos de BuildConfigure the Publish Build Artifacts task

O pipeline MSIX padrão não salva os artefatos gerados.The default MSIX pipeline does not save the generated artifacts. Para adicionar recursos de publicação à sua definição YAML, adicione as tarefas a seguir.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)'

Você pode ver os artefatos gerados na opção Artefatos da página de resultados do build.You can see the generated artifacts in the Artifacts option of the build results page.

Arquivo AppInstaller para distribuição fora da Microsoft StoreAppInstaller file for non-store distribution

Se estiver distribuindo o aplicativo fora da Microsoft Store, será possível utilizar o arquivo AppInstaller para instalação e atualizações do pacote.If you're distributing your application outside the Store you can take advantage of the AppInstaller file for your package install and updates

Um arquivo .appinstaller que buscará arquivos atualizados em \server\fooAn .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>

O elemento UpdateSettings é usado para informar ao sistema quando verificar por atualizações e se é necessário forçar o usuário a atualizar.The UpdateSettings element is used to tell the system when to check for updates and whether to force the user to update. A referência de esquema completa, incluindo os namespaces compatíveis com cada versão do Windows 10, pode ser encontrada nos documentos em 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.

Se você adicionar o arquivo .appinstaller ao projeto de empacotamento e definir a propriedade Package Action dele como Content e a propriedade Copy to Output Directory como Copy if newer, será possível adicionar outra tarefa do PowerShell ao arquivo YAML que atualiza os atributos Version da raiz e os elementos MainPackage e salva o arquivo atualizado no diretório de teste: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'

Em seguida, você distribuiria o arquivo .appinstaller para os usuários finais e permitiria que eles clicassem duas vezes nele em vez de no arquivo .msix para instalar o aplicativo empacotado.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.

Implantação contínuaContinuous Deployment

O próprio arquivo do instalador é um arquivo XML não compilado que pode ser editado depois do build, caso necessário.The app installer file itself is an uncompiled XML file that can be edited after the build, if required. Isso facilita a utilização quando você implanta o software em diversos ambientes e quando deseja separar o pipeline do build pelo processo de lançamento.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.

Se você criar um pipeline de lançamento no Azure Portal usando o modelo “Trabalho vazio” e usar o pipeline de build recém-configurado como a origem do artefato a ser implantado, será possível adicionar a tarefa PowerShell ao estágio de lançamento para alterar dinamicamente os valores dos dois atributos Uri no arquivo .appinstaller para refletir a localização na qual o aplicativo foi publicado.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.

Uma tarefa de pipeline de lançamento que modifica os Uris no arquivo .appinstallerA 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'

Na tarefa acima, o URI é configurado no caminho UNC de um compartilhamento de arquivos do Azure.In the task above, the URI is set to the UNC path of an Azure file share. Como esse seria o local no qual o sistema operacional procuraria pelo pacote MSIX quando o aplicativo fosse instalado ou atualizado, também adicionei outro script de linha de comando ao pipeline de lançamento que primeiro mapeia o compartilhamento de arquivos na nuvem para a unidade Z:\ local no agente do build antes de usar o comando xcopy para copiar os arquivos .appinstaller e .msix para lá: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'

Caso você hospede seu próprio Azure DevOps Server local, será possível publicar os arquivos em seu próprio compartilhamento de rede interno.If you host your own on-premises Azure DevOps Server, you may of course publish the files to your own internal network share.

Se você optar por publicar em um servidor Web, será possível informar ao MSBuild para gerar um arquivo .appinstaller versionado e uma página HTML que contenha um link de download e algumas informações sobre o aplicativo empacotado fornecendo alguns argumentos adicionais no arquivo 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'

O arquivo HTML gerado inclui um hiperlink prefixado com o esquema de ativação de protocolo ms-appinstaller que independe do navegador: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>

Se você configurar um pipeline de lançamento que publica o conteúdo da pasta de destino na intranet ou em qualquer outro site e o servidor Web oferecer suporte a solicitações de intervalo de bytes e estiver configurado corretamente, os usuários finais poderão usar esse link para instalar o aplicativo diretamente sem baixar o pacote MSIX primeiro.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.