Configurare una pipeline CI/CD per automatizzare le build e le distribuzioni MSIXSet up a CI/CD pipeline to automate your MSIX builds and deployments

È possibile usare Azure Pipelines per creare build automatiche per il progetto MSIX.You can use Azure Pipelines to create automated builds for your MSIX project. Questo articolo illustra come eseguire questa operazione in Azure DevOps.This article takes a look at how to do so in Azure DevOps. Ti mostreremo anche come eseguire queste attività usando la riga di comando, in modo da poter integrare la soluzione con qualsiasi altro sistema di compilazione.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.

Creare una nuova pipeline di AzureCreate a new Azure Pipeline

Per iniziare, iscriviti ad Azure Pipelines se non lo hai ancora fatto.Begin by signing up for Azure Pipelines if you haven't done so already.

Crea quindi una pipeline per poter compilare il tuo codice sorgente.Next, create a pipeline that you can use to build your source code. Per un'esercitazione sulla creazione di una pipeline per creare un repository GitHub, vedi Creare la prima pipeline.For a tutorial about building a pipeline to build a GitHub repository, see Create your first pipeline. Azure Pipelines supporta i tipi di repository elencati in questo articolo.Azure Pipelines supports the repository types listed in this article.

Per configurare la pipeline di compilazione effettiva, passa al portale di Azure DevOps all'indirizzo dev.azure.com/<organization> e crea un nuovo progetto.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 non hai un account, puoi crearne uno gratuitamente.If you don’t have an account, you can create one for free. Dopo aver completato l'accesso e aver creato un progetto, sarà possibile eseguire il push del codice sorgente nel repository Git configurato all'indirizzo https://<organization>@dev.azure.com/<organization>/<project>/_git/<project> o usare qualsiasi altro provider, ad esempio 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. Il percorso del repository potrà essere scelto durante la creazione di una nuova pipeline nel portale facendo prima clic sul pulsante Pipelines e quindi su Nuova 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.

Nella schermata di configurazione successivamente visualizzata selezionare l'opzione File YAML di Azure Pipelines esistente e scegliere il percorso del file YAML archiviato nel repository.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.

Aggiungere il certificato del progetto alla libreria File protettiAdd your project certificate to the Secure files library

Nota

È consigliabile evitare di inviare certificati al repository, se possibile. Git li ignora per impostazione predefinita.You should avoid submitting certificates to your repo if at all possible, and git ignores them by default. Per la gestione sicura dei file riservati, ad esempio i certificati, Azure DevOps supporta la funzionalità File protetti.To manage the safe handling of sensitive files like certificates, Azure DevOps supports the secure files feature.

Per caricare un certificato per la build automatizzata:To upload a certificate for your automated build:

  1. In Azure Pipelines espandi Pipeline nel riquadro di spostamento e fai clic su Libreria.In Azure Pipelines, expand Pipelines in the navigation pane and click Library.
  2. Fai clic sulla scheda File protetti e quindi su + File protetto.Click the Secure files tab and then click + Secure file.
  3. Seleziona il file di certificato e fai clic su OK.Browse to the certificate file and click OK.
  4. Dopo aver caricato il certificato, selezionalo per visualizzarne le proprietà.After you upload the certificate, select it to view its properties. In Autorizzazioni pipeline abilita l'interruttore Autorizza per l'uso in tutte le pipeline.Under Pipeline permissions, enable the Authorize for use in all pipelines toggle.
  5. Se la chiave privata del certificato ha una password, consigliamo di archiviare tale password in Azure Key Vault e quindi collegarla a un gruppo di variabili.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. Puoi usare la variabile per accedere alla password dalla pipeline.You can use the variable to access the password from the pipeline. Si noti che una password è supportata solo per la chiave privata. L'uso di un file di certificato protetto da password non è attualmente supportato.Note that a password is only supported for the private key; using a certificate file that is itself password-protected is not currently supported.

Nota

A partire da Visual Studio 2019, nei progetti MSIX non viene più generato un certificato temporaneo.Starting in Visual Studio 2019, a temporary certificate is no longer generated in MSIX projects. Per creare o esportare certificati, usa i cmdlet di PowerShell descritti in questo articolo.To create or export certificates, use the PowerShell cmdlets described in this article.

Configurare la build nel file YAMLConfigure the Build in your YAML file

La tabella seguente elenca i diversi argomenti di MSBuild che puoi definire per configurare la pipeline di compilazione.The table below lists the different MSBuild arguments you can define to setup your build pipeline.

Argomento di MSBuildMSBuild argument ValoreValue DescrizioneDescription
AppxPackageDirAppxPackageDir $(Build.ArtifactStagingDirectory)\AppxPackages$(Build.ArtifactStagingDirectory)\AppxPackages Definisce la cartella in cui archiviare gli elementi generati.Defines the folder to store the generated artifacts.
AppxBundlePlatformsAppxBundlePlatforms $(Build.BuildPlatform)$(Build.BuildPlatform) Ti consente di definire le piattaforme da includere nel bundle.Enables you to define the platforms to include in the bundle.
AppxBundleAppxBundle SempreAlways Crea un file con estensione msixbundle o appxbundle con i file con estensione msix o appx per la piattaforma specificata.Creates an .msixbundle/.appxbundle with the .msix/.appx files for the platform specified.
UapAppxPackageBuildModeUapAppxPackageBuildMode StoreUploadStoreUpload Genera il file con estensione msixupload o appxupload e la cartella _Test per il trasferimento locale.Generates the .msixupload/.appxupload file and the _Test folder for sideloading.
UapAppxPackageBuildModeUapAppxPackageBuildMode CICI Genera solo il file con estensione msixupload o appxupload.Generates the .msixupload/.appxupload file only.
UapAppxPackageBuildModeUapAppxPackageBuildMode SideloadOnlySideloadOnly Genera solo la cartella _Test per il trasferimento locale.Generates the _Test folder for sideloading only.
AppxPackageSigningEnabledAppxPackageSigningEnabled Truetrue Abilita la firma del pacchetto.Enables package signing.
PackageCertificateThumbprintPackageCertificateThumbprint Identificazione personale del certificatoCertificate Thumbprint Questo valore deve corrispondere all'identificazione personale nel certificato di firma oppure la stringa deve essere vuota.This value must match the thumbprint in the signing certificate, or be an empty string.
PackageCertificateKeyFilePackageCertificateKeyFile PathPath Percorso del certificato da usare.The path to the certificate to use. Viene recuperato dai metadati del file protetto.This is retrieved from the secure file metadata.
PackageCertificatePasswordPackageCertificatePassword PasswordPassword Password per la chiave privata del certificato.The password for the private key in the certificate. Consigliamo di archiviare la password in Azure Key Vault e collegarla a un gruppo di variabili.We recommend that you store your password in Azure Key Vault and link the password to variable group. Puoi passare la variabile a questo argomento.You can pass the variable to this argument.

Prima di compilare il progetto di creazione del pacchetto come fa la procedura guidata di Visual Studio tramite la riga di comando di MSBuild, il processo di compilazione può eseguire il controllo della versione del pacchetto MSIX generato modificando l'attributo Version dell'elemento Package nel file 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. In Azure Pipelines è possibile ottenere questo risultato usando un'espressione per impostare una variabile contatore che viene incrementata ad ogni compilazione e uno script di PowerShell che usa la classe System.Xml.Linq.XDocument in .NET per modificare il valore dell'attributo.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.

File YAML di esempio che definisce la pipeline di compilazione 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'

Di seguito sono riportate le scomposizioni delle diverse attività di compilazione definite nel file YAML:Below are breakdowns of the different Build tasks defined in the YAMl file:

Configurare le proprietà di generazione del pacchettoConfigure package generation properties

La definizione seguente imposta la directory dei componenti di build, la piattaforma e definisce se deve essere compilato o meno un bundle.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

Configurare la firma del pacchettoConfigure package signing

Per firmare il pacchetto MSIX o APPX, la pipeline deve recuperare il certificato di firma.To sign the MSIX (or APPX) package the pipeline needs to retrieve the signing certificate. A tale scopo, aggiungi un'attività DownloadSecureFile prima dell'attività VSBuild.To do this, add a DownloadSecureFile task prior to the VSBuild task. In questo modo, potrai accedere al certificato di firma tramite 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'

Aggiorna quindi l'attività MSBuild in modo da fare riferimento al certificato di firma: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)"'

Nota

Come precauzione, l'argomento PackageCertificateThumbprint viene intenzionalmente lasciato vuoto.The PackageCertificateThumbprint argument is intentionally set to an empty string as a precaution. Se l'identificazione personale è impostata nel progetto ma non corrisponde al certificato di firma, la build non verrà eseguita e verrà visualizzato l'errore: 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.

Verificare i parametriReview parameters

I parametri definiti con la sintassi $() sono variabili specificate nella definizione della build e saranno diversi in altri sistemi di compilazione.The parameters defined with the $() syntax are variables defined in the build definition, and will change in other build systems.

Per visualizzare tutte le variabili predefinite, vedi Variabili di compilazione predefinite.To view all predefined variables, see Predefined build variables.

Configurare l'attività Pubblica artefatti di compilazioneConfigure the Publish Build Artifacts task

La pipeline MSIX predefinita non salva gli artefatti generati.The default MSIX pipeline does not save the generated artifacts. Per aggiungere le funzionalità di pubblicazione alla definizione YAML, aggiungi le attività seguenti.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)'

Puoi visualizzare gli artefatti generati nell'opzione Artefatti della pagina relativa ai risultati della compilazione.You can see the generated artifacts in the Artifacts option of the build results page.

File AppInstaller per la distribuzione non nello StoreAppInstaller file for non-store distribution

Se stai distribuendo l'applicazione all'esterno dello Store, puoi sfruttare il file AppInstaller per l'installazione e gli aggiornamenti del pacchetto.If you're distributing your application outside the Store you can take advantage of the AppInstaller file for your package install and updates

File con estensione appinstaller che cerca i file aggiornati in \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>

L'elemento UpdateSettings viene usato per indicare al sistema quando verificare la disponibilità di aggiornamenti e se forzare l'esecuzione dell'aggiornamento da parte dell'utente.The UpdateSettings element is used to tell the system when to check for updates and whether to force the user to update. Per il riferimento dello schema completo, inclusi gli spazi dei nomi supportati per ogni versione di Windows 10, vedi la documentazione in 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 aggiungi il file con estensione appinstaller al progetto di creazione del pacchetto e imposti la proprietà Package Action su Content e la proprietà Copy to Output Directory su Copy (se più recente), puoi aggiungere un'altra attività di PowerShell al file YAML che aggiorni gli attributi di Version della radice e gli elementi MainPackage e salvi il file aggiornato nella directory di gestione temporanea: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'

Potresti quindi distribuire il file con estensione appinstaller agli utenti finali e consentire loro di installare l'app in pacchetto facendo doppio clic su questo file anziché su quello con estensione 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.

Distribuzione continuaContinuous Deployment

Il file del programma di installazione dell'app è un file XML non compilato che può essere modificato dopo la build, se necessario.The app installer file itself is an uncompiled XML file that can be edited after the build, if required. In questo modo, è facile da usare quando distribuisci il software in più ambienti e quando vuoi separare la pipeline di compilazione dal processo di rilascio.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 crei una pipeline di rilascio nel portale di Azure usando il modello di processo vuoto e usi la pipeline di compilazione recentemente configurata come origine dell'artefatto da distribuire, potrai aggiungere l'attività di PowerShell alla fase di rilascio per modificare dinamicamente i valori dei due attributi Uri nel file con estensione appinstaller in modo da riflettere la posizione in cui viene pubblicata l'app.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.

Attività della pipeline di rilascio che modifica gli attributi Uri nel file con estensione 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'

Nell'attività precedente l'URI è impostato sul percorso UNC di una condivisione file di Azure.In the task above, the URI is set to the UNC path of an Azure file share. Poiché questo è il punto in cui il sistema operativo cercherà il pacchetto MSIX quando installi e aggiorni l'app, abbiamo aggiunto anche un altro script della riga di comando alla pipeline di rilascio che esegue il mapping della condivisione file nel cloud all'unità Z:\ locale nell'agente di compilazione prima di usare il comando xcopy per copiare i file con estensione appinstaller e 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'

Se ospiti la tua istanza di Azure DevOps Server locale, puoi naturalmente pubblicare i file nella condivisione di rete interna.If you host your own on-premises Azure DevOps Server, you may of course publish the files to your own internal network share.

Se scegli di eseguire la pubblicazione in un server Web, puoi indicare a MSBuild di generare un file appinstaller con controllo delle versioni, una pagina HTML con un collegamento di download e alcune informazioni sull'app in pacchetto fornendo alcuni argomenti aggiuntivi nel file 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'

Il file HTML generato include un collegamento ipertestuale preceduto dallo schema di attivazione di protocolli di ms-appinstaller indipendente dal browser: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 configuri una pipeline di versione che pubblica il contenuto della cartella di ricezione nella intranet o in un altro sito Web e il server Web supporta le richieste di intervalli di byte ed è configurato correttamente, gli utenti finali possono usare questo collegamento per installare direttamente l'app senza prima scaricare il pacchetto 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.