MSIX 빌드 및 배포를 자동화하는 CI/CD 파이프라인 설정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.

새 Azure Pipelines 만들기Create 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 리포지토리를 빌드하기 위한 파이프라인을 빌드하는 방법에 대한 자습서는 첫 번째 파이프라인 만들기를 참조하세요.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.

실제 빌드 파이프라인을 설정하려면 dev.azure.com/<organization>에서 Azure DevOps 포털로 이동하여 새 프로젝트를 만듭니다.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. 로그인하여 프로젝트를 만든 후에는 소스 코드를 https://<organization>@dev.azure.com/<organization>/<project>/_git/<project>에 설정된 Git 리포지토리로 푸시하거나 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.

다음에 나오는 구성 화면에서 기존 Azure Pipelines YAML 파일 옵션을 선택하고, 리포지토리에서 체크 인된 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. 보안 파일 탭을 클릭한 다음, + 보안 파일을 클릭합니다.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 cmdlet을 사용하세요.To create or export certificates, use the PowerShell cmdlets described in this article.

YAML 파일에서 빌드 구성Configure the Build in your YAML file

아래 표에는 빌드 파이프라인을 설정하기 위해 정의할 수 있는 다양한 MSBuild 인수가 나와 있습니다.The table below lists the different MSBuild arguments you can define to setup your build pipeline.

MSBuild 인수MSBuild 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 지정된 플랫폼용 .msix/.appx 파일을 사용하여 .msixbundle/.appxbundle을 만듭니다.Creates an .msixbundle/.appxbundle with the .msix/.appx files for the platform specified.
UapAppxPackageBuildModeUapAppxPackageBuildMode StoreUploadStoreUpload .msixupload/.appxupload 파일 및 사이드로드용 _Test 폴더를 생성합니다.Generates the .msixupload/.appxupload file and the _Test folder for sideloading.
UapAppxPackageBuildModeUapAppxPackageBuildMode CICI .msixupload/.appxupload 파일만 생성합니다.Generates the .msixupload/.appxupload file only.
UapAppxPackageBuildModeUapAppxPackageBuildMode SideloadOnlySideloadOnly 사이드로드 전용 _Test 폴더를 생성합니다.Generates the _Test folder for sideloading only.
AppxPackageSigningEnabledAppxPackageSigningEnabled truetrue 패키지 서명을 사용하도록 설정합니다.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 패키지의 버전을 지정하도록 Package.appxmanifest 파일에 있는 Package 요소의 Version 특성을 편집할 수 있습니다.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에서는 모든 빌드에 대해 증분하는 카운터 변수를 설정하는 식 및 .NET의 System.Xml.Linq.XDocument 클래스를 사용하여 특성 값을 변경하는 PowerShell 스크립트를 사용하여 이 작업을 수행할 수 있습니다.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.

MSIX 빌드 파이프라인을 정의하는 YAML 파일 샘플Sample 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. 이렇게 하려면 VSBuild 작업 전에 DownloadSecureFile 작업을 추가합니다.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.

미리 정의된 변수를 모두 보려면 미리 정의된 빌드 변수를 참조하세요.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 파일AppInstaller file for non-store distribution

애플리케이션을 스토어 외부에 배포하는 경우 AppInstaller 파일을 패키지 설치 및 업데이트에 활용할 수 있습니다.If you're distributing your application outside the Store you can take advantage of the AppInstaller file for your package install and updates

다음은 \server\foo에서 업데이트된 파일을 찾는 .appinstaller 파일입니다.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 파일을 패키징 프로젝트에 추가하고 해당 [패키지 작업] 속성을 Content로 설정하고 [출력 디렉터리에 복사] 속성을 Copy로 설정하는 경우(최신 버전이 있는 경우), 루트 및 MainPackage 요소의 Version 특성을 업데이트하고 업데이트된 파일을 준비 디렉터리에 저장하는 다른 PowerShell 작업을 YAML 파일에 추가할 수 있습니다.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 Portal에서 "빈 작업" 템플릿을 사용하여 릴리스 파이프라인을 만들고 최근에 설정된 빌드 파이프라인을 배포할 아티팩트의 원본으로 사용하는 경우, .appinstaller 파일에서 두 개의 URI 특성 값을 동적으로 변경하여 앱이 게시되는 위치를 반영하기 위해 PowerShell 작업을 릴리스 단계에 추가할 수 있습니다.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.

.appinstaller 파일에서 URI를 수정하는 릴리스 파이프라인 작업은 다음과 같습니다.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는 Azure 파일 공유의 UNC 경로로 설정됩니다.In the task above, the URI is set to the UNC path of an Azure file share. 이 위치에서는 앱을 설치하고 업데이트할 때 OS에서 MSIX 패키지를 찾을 수 있으므로, xcopy 명령을 사용하여 .appinstaller 및 .msix 파일을 복사하기 전에 먼저 클라우드의 파일 공유를 빌드 에이전트의 로컬 Z:\ 드라이브에 매핑하는 다른 명령줄 스크립트도 릴리스 파이프라인에 추가했습니다.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 서버를 호스팅하는 경우 파일을 사용자 고유의 내부 네트워크 공유에 게시할 수도 있습니다.If you host your own on-premises Azure DevOps Server, you may of course publish the files to your own internal network share.

웹 서버에 게시하도록 선택한 경우 YAML 파일에서 몇 가지 추가 인수를 제공하여 버전이 지정된 .appinstaller 파일 및 패키지된 앱에 대한 다운로드 링크와 일부 정보가 포함된 HTML 페이지를 생성하도록 MSBuild에 지시할 수 있습니다.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>

drop 폴더의 콘텐츠를 인트라넷 또는 다른 웹 사이트에 게시하는 릴리스 파이프라인을 설정하고 웹 서버에서 바이트 범위 요청을 지원하며 올바르게 구성된 경우, 최종 사용자는 이 링크를 사용하여 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.