YAML 파일로 CI/CD 파이프라인 구성

아래 표에는 빌드 파이프라인을 설정하기 위해 정의할 수 있는 다양한 MSBuild 인수가 나와 있습니다.

MSBuild 인수 Description
AppxPackageDir $(Build.ArtifactStagingDirectory)\AppxPackages 생성된 아티팩트를 저장할 폴더를 정의합니다.
AppxBundlePlatforms $(Build.BuildPlatform) 번들에 포함할 플랫폼을 정의할 수 있습니다.
.appxbundle 항상 표시 지정된 플랫폼용 .msix/.appx 파일을 사용하여 .msixbundle/.appxbundle을 만듭니다.
UapAppxPackageBuildMode StoreUpload .msixupload/.appxupload 파일 및 사이드로드용 _Test 폴더를 생성합니다.
UapAppxPackageBuildMode CI .msixupload/.appxupload 파일만 생성합니다.
UapAppxPackageBuildMode SideloadOnly 사이드로드 전용 _Test 폴더를 생성합니다.
AppxPackageSigningEnabled true 패키지 서명을 사용하도록 설정합니다.
PackageCertificateThumbprint 인증서 지문 이 값은 서명 인증서의 지문과 일치하거나 빈 문자열이어야 합니다.
PackageCertificateKeyFile Path 사용할 인증서의 경로입니다. 보안 파일 메타데이터에서 검색됩니다.
PackageCertificatePassword Password 인증서의 프라이빗 키용 암호입니다. 암호를 Azure Key Vault에 저장하고 이 암호를 변수 그룹에 연결하는 것이 좋습니다. 변수를 이 인수에 전달할 수 있습니다.

빌드 프로세스는 Visual Studio의 마법사에서 MSBuild 명령줄을 사용하는 것과 동일한 방식으로 패키징 프로젝트를 빌드하기 전에 생성되는 MSIX 패키지의 버전을 지정하도록 Package.appxmanifest 파일에 있는 Package 요소의 Version 특성을 편집할 수 있습니다. Azure Pipelines에서는 모든 빌드에 대해 증분하는 카운터 변수를 설정하는 식 및 .NET의 System.Xml.Linq.XDocument 클래스를 사용하여 특성 값을 변경하는 PowerShell 스크립트를 사용하여 이 작업을 수행할 수 있습니다.

MSIX 빌드 파이프라인을 정의하는 YAML 파일 샘플

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 파일에 정의된 여러 빌드 작업에 대한 분석입니다.

패키지 생성 속성 구성

아래 정의는 빌드 구성 요소 및 플랫폼의 디렉터리를 설정하고 번들을 빌드할지 여부를 정의합니다.

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

패키지 서명 구성

MSIX(또는 APPX) 패키지에 서명하려면 파이프라인에서 서명 인증서를 검색해야 합니다. 이렇게 하려면 VSBuild 작업 전에 DownloadSecureFile 작업을 추가합니다. 그러면 signingCert를 통해 서명 인증서에 액세스할 수 있습니다.

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

다음으로 서명 인증서를 참조하도록 MSBuild 작업을 업데이트합니다.

- 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 인수는 예방 조치를 위해 의도적으로 빈 문자열로 설정됩니다. 지문이 프로젝트에 설정되었지만 서명 인증서와 일치하지 않으면 Certificate does not match supplied signing thumbprint 오류로 인해 빌드가 실패합니다.

매개 변수 검토

$() 구문으로 정의된 매개 변수는 빌드 정의에 정의된 변수이며, 다른 빌드 시스템에서 변경됩니다.

미리 정의된 변수를 모두 보려면 미리 정의된 빌드 변수를 참조하세요.

빌드 아티팩트 게시 작업 구성

기본 MSIX 파이프라인은 생성된 아티팩트를 저장하지 않습니다. 게시 기능을 YAML 정의에 추가하려면 다음 작업을 추가합니다.

- 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)'

생성된 아티팩트는 빌드 결과 페이지의 아티팩트 옵션에서 확인할 수 있습니다.

비 스토어 배포용 AppInstaller 파일

애플리케이션을 스토어 외부에 배포하는 경우 AppInstaller 파일을 패키지 설치 및 업데이트에 활용할 수 있습니다.

다음은 \server\foo에서 업데이트된 파일을 찾는 .appinstaller 파일입니다.

<?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 요소는 업데이트를 확인하는 시기 및 사용자가 강제로 업데이트하도록 할지 여부를 시스템에 알리는 데 사용됩니다. 각 Windows 10 버전에 지원되는 네임스페이스를 포함한 전체 스키마 참조는 bit.ly/2TGWnCR의 문서에서 확인할 수 있습니다.

.appinstaller 파일을 패키징 프로젝트에 추가하고 해당 [패키지 작업] 속성을 Content로 설정하고 [출력 디렉터리에 복사] 속성을 Copy로 설정하는 경우(최신 버전이 있는 경우), 루트 및 MainPackage 요소의 Version 특성을 업데이트하고 업데이트된 파일을 준비 디렉터리에 저장하는 다른 PowerShell 작업을 YAML 파일에 추가할 수 있습니다.

- 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 파일 대신 이 파일을 두 번 클릭하여 패키지된 앱을 설치합니다.

지속적인 배포

앱 설치 관리자 파일 자체는 필요한 경우 빌드 후에 편집할 수 있는 컴파일되지 않은 XML 파일입니다. 이렇게 하면 소프트웨어를 여러 환경에 배포하는 경우와 릴리스 프로세스에서 빌드 파이프라인을 분리하려는 경우에 쉽게 사용할 수 있습니다.

Azure Portal에서 "빈 작업" 템플릿을 사용하여 릴리스 파이프라인을 만들고 최근에 설정된 빌드 파이프라인을 배포할 아티팩트의 원본으로 사용하는 경우, .appinstaller 파일에서 두 개의 URI 특성 값을 동적으로 변경하여 앱이 게시되는 위치를 반영하기 위해 PowerShell 작업을 릴리스 단계에 추가할 수 있습니다.

.appinstaller 파일에서 URI를 수정하는 릴리스 파이프라인 작업은 다음과 같습니다.

- 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 경로로 설정됩니다. 이 위치에서는 앱을 설치하고 업데이트할 때 OS에서 MSIX 패키지를 찾을 수 있으므로, xcopy 명령을 사용하여 .appinstaller 및 .msix 파일을 복사하기 전에 먼저 클라우드의 파일 공유를 빌드 에이전트의 로컬 Z:\ 드라이브에 매핑하는 다른 명령줄 스크립트도 릴리스 파이프라인에 추가했습니다.

- 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 서버를 호스팅하는 경우 파일을 사용자의 내부 네트워크 공유에 게시할 수도 있습니다.

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

- 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 프로토콜 활성화 체계를 접두사로 사용하는 하이퍼링크가 포함되어 있습니다.

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

drop 폴더의 콘텐츠를 인트라넷 또는 다른 웹 사이트에 게시하는 릴리스 파이프라인을 설정하고 웹 서버에서 바이트 범위 요청을 지원하며 올바르게 구성된 경우, 최종 사용자는 이 링크를 사용하여 MSIX 패키지를 먼저 다운로드하지 않고도 앱을 직접 설치할 수 있습니다.