# Commit triggers
trigger:
- master

# PR triggers
pr:
  branches:
    include:
    - master
  paths:
    include:
    - azure-pipelines.yml
    - src/*
    - schemas/JSON/manifests/*

pool:
  vmImage: 'windows-latest'

variables:
  solution: 'src\AppInstallerCLI.sln'
  appxPackageDir: '$(Build.ArtifactStagingDirectory)/AppxPackages/'

# Do not set the build version for a PR build.

jobs:
- job: 'GetReleaseTag'
  condition: not(eq(variables['Build.Reason'], 'PullRequest'))
  variables:
    runCodesignValidationInjection: ${{ false }}
    skipComponentGovernanceDetection: ${{ true }}
  steps:
  - task: PowerShell@2
    name: 'GetTag'
    displayName: Get Release Tag
    inputs:
      filePath: 'src\binver\Update-BinVer.ps1'
      arguments: '-OutVar'
      workingDirectory: 'src'

- job: 'Build'
  timeoutInMinutes: 120
  dependsOn: 'GetReleaseTag'
  condition: always()

  strategy:
    matrix:
      x86_release:
        buildConfiguration: 'Release'
        buildPlatform: 'x86'
        testBuildConfiguration: 'TestRelease'
      x64_release:
        buildConfiguration: 'Release'
        buildPlatform: 'x64'
        testBuildConfiguration: 'TestRelease'

  variables:
    BuildVer: $[counter(dependencies.GetReleaseTag.outputs['GetTag.tag'], 1)]
    buildOutDir: $(Build.SourcesDirectory)\src\$(buildPlatform)\$(buildConfiguration)
    buildOutDirAnyCpu: $(Build.SourcesDirectory)\src\AnyCPU\$(buildConfiguration)
    artifactsDir: $(Build.ArtifactStagingDirectory)\$(buildPlatform)
    packageLayoutDir: $(Build.BinariesDirectory)\WingetPackageLayout

  steps:
  - task: NuGetToolInstaller@1
    displayName: Install Nuget

  # Restores all projects, including native (vcxproj) projects
  - task: NuGetCommand@2
    displayName: Restore Solution
    inputs:
      restoreSolution: '$(solution)'

  # Restore these UAP packages as https://github.com/NuGet/Home/issues/7796 leads to all UAP packages being skipped for restore.
  # Even though they don't need any actual restore action, they need the project.assets.json file to be created and a direct restore does that.
  - task: NuGetCommand@2
    displayName: Restore AppInstallerCLIPackage
    inputs:
      restoreSolution: 'src\AppInstallerCLIPackage\AppInstallerCLIPackage.wapproj'

  - task: NuGetCommand@2
    displayName: Restore AppInstallerTestMsixInstaller
    inputs:
      restoreSolution: 'src\AppInstallerTestMsixInstaller\AppInstallerTestMsixInstaller.wapproj'

  # Restores only .NET core projects, but is still necessary, as without this the IndexCreationTool and LocalhostWebServer projects fail to build
  - task: DotNetCoreCLI@2
    displayName: DotNet Restore
    inputs:
      command: 'restore'
      projects: '**/*.csproj'

  - task: PowerShell@2
    displayName: Update Binary Version
    condition: not(eq(variables['Build.Reason'], 'PullRequest'))
    inputs:
      filePath: 'src\binver\Update-BinVer.ps1'
      arguments: '-TargetFile binver\binver\version.h -BuildVersion $(BuildVer)'
      workingDirectory: 'src'

  # Build all solutions in the root directory.
  - task: VSBuild@1
    displayName: Build Solution
    inputs:
      platform: '$(buildPlatform)'
      solution: '$(solution)'
      configuration: '$(buildConfiguration)'
      msbuildArgs: '/bl:$(artifactsDir)\msbuild.binlog
                    /p:AppxBundlePlatforms="$(buildPlatform)"
                    /p:AppxPackageDir="$(appxPackageDir)"
                    /p:AppxBundle=Always
                    /p:UapAppxPackageBuildMode=SideloadOnly'

  - task: VSBuild@1
    displayName: Build Test Project
    inputs:
      platform: '$(buildPlatform)'
      solution: '$(solution)'
      configuration: '$(testBuildConfiguration)'
      msbuildArgs: '/bl:$(artifactsDir)\msbuild-testProject.binlog
                    /p:AppxBundlePlatforms="$(buildPlatform)"
                    /p:AppxPackageDir="$(appxPackageDir)"
                    /p:AppxBundle=Always
                    /p:UapAppxPackageBuildMode=SideloadOnly'

  - task: CopyFiles@2
    displayName: 'Copy Symbols to artifacts folder'
    inputs:
      Contents: |
          $(buildOutDir)\WindowsPackageManager\WindowsPackageManager.pdb
          $(buildOutDir)\Microsoft.Management.Configuration\Microsoft.Management.Configuration.pdb
      TargetFolder: '$(artifactsDir)'
    condition: succeededOrFailed()

  - task: PowerShell@2
    displayName: Install Tests Dependencies
    inputs:
      targetType: 'inline'
      script: |
        Get-ChildItem AppInstallerCLIPackage_0.0.2.0_Test\Dependencies\$(buildPlatform) -Filter *.appx | %{ Add-AppxPackage $_.FullName }
      workingDirectory: $(appxPackageDir)

  - task: VisualStudioTestPlatformInstaller@1
    displayName: Prepare VSTest for E2E Tests
    inputs:
      packageFeedSelector: 'nugetOrg'

  - task: DownloadSecureFile@1
    name: PsExec
    displayName: 'Download PsExec.exe'
    inputs:
      secureFile: 'PsExec.exe'

  - task: CmdLine@2
    displayName: Run Unit Tests Unpackaged Under System Context
    inputs:
      script: |
        $(PsExec.secureFilePath) -accepteula -s -i $(buildOutDir)\AppInstallerCLITests\AppInstallerCLITests.exe -logto $(artifactsDir)\AICLI-Unpackaged-System.log -s -r junit -o $(artifactsDir)\TEST-AppInstallerCLI-Unpackaged-System.xml
      workingDirectory: '$(buildOutDir)\AppInstallerCLITests'
    continueOnError: true

  - task: PowerShell@2
    displayName: Create Package Layout
    inputs:
      filePath: 'src\AppInstallerCLIPackage\Execute-AppxRecipe.ps1'
      arguments: '-AppxRecipePath AppInstallerCLIPackage\bin\$(buildPlatform)\$(buildConfiguration)\AppInstallerCLIPackage.build.appxrecipe -LayoutPath $(packageLayoutDir) -Force -Verbose'
      workingDirectory: 'src'
    continueOnError: true

  - task: PowerShell@2
    displayName: Run Unit Tests Packaged
    inputs:
      filePath: 'src\AppInstallerCLITests\Run-TestsInPackage.ps1'
      arguments: '-Args "~[pips]" -BuildRoot $(buildOutDir) -PackageRoot $(packageLayoutDir) -LogTarget $(artifactsDir)\AICLI-Packaged.log -TestResultsTarget $(artifactsDir)\TEST-AppInstallerCLI-Packaged.xml -ScriptWait'
      workingDirectory: 'src'
    continueOnError: true

  - task: PublishTestResults@2
    displayName: Publish Unit Test Results
    inputs:
      testResultsFormat: 'JUnit'
      testResultsFiles: '$(artifactsDir)\TEST-*.xml'
      failTaskOnFailedTests: true

  - task: MSBuild@1
    displayName: Build MSIX Test Installer File
    inputs:
      platform: '$(buildPlatform)'
      solution: 'src\AppInstallerTestMsixInstaller\AppInstallerTestMsixInstaller.wapproj'
      configuration: '$(buildConfiguration)'
      msbuildArguments: '/p:AppxPackageOutput="$(Build.ArtifactStagingDirectory)\AppInstallerTestMsixInstaller.msix"
                         /p:AppxBundle=Never
                         /p:UapAppxPackageBuildMode=SideLoadOnly
                         /p:AppxPackageSigningEnabled=false'

  - task: PowerShell@2
    displayName: 'Set program files directory'
    inputs:
      targetType: 'inline'
      script: |
        if ("$(buildPlatform)" -eq "x86") {
          Write-Host "##vso[task.setvariable variable=platformProgramFiles;]${env:ProgramFiles(x86)}"
        } else {
          Write-Host "##vso[task.setvariable variable=platformProgramFiles;]${env:ProgramFiles}"
        }

  # Resolves resource strings utilized by InProc E2E tests.
  - task: CopyFiles@2
    displayName: 'Copy resources.pri to dotnet directory'
    inputs:
      SourceFolder: '$(buildOutDir)\AppInstallerCLI'
      TargetFolder:  '$(platformProgramFiles)\dotnet'
      Contents: resources.pri

  # Winmd accessed by test runner process (dotnet.exe)
  - task: CopyFiles@2
    displayName: 'Copy winmd to dotnet directory'
    inputs:
      SourceFolder: '$(buildOutDir)\Microsoft.Management.Deployment'
      TargetFolder: '$(platformProgramFiles)\dotnet'
      Contents: Microsoft.Management.Deployment.winmd

  - template: templates/e2e-setup.yml
    parameters:
      sourceDir: $(Build.SourcesDirectory)
      localhostWebServerArgs: '-BuildRoot $(buildOutDir)\LocalhostWebServer -StaticFileRoot $(Agent.TempDirectory)\TestLocalIndex -LocalSourceJson $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\localsource.json -TestDataPath $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData -SourceCert $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTest.cer'

  - template: templates/e2e-test.template.yml
    parameters:
      title: "E2E Tests Packaged"
      isPackaged: true
      filter: "TestCategory!=InProcess&TestCategory!=OutOfProcess"

  - template: templates/e2e-test.template.yml
    parameters:
      title: "Microsoft.Management.Deployment E2E Tests (In-process)"
      isPackaged: false
      filter: "TestCategory=InProcess"

  - template: templates/e2e-test.template.yml
    parameters:
      title: "Microsoft.Management.Deployment E2E Tests (Out-of-process)"
      isPackaged: true
      filter: "TestCategory=OutOfProcess"

  - task: CopyFiles@2
    displayName: 'Copy E2E Tests Package Log to artifacts folder'
    inputs:
      SourceFolder: '$(temp)\E2ETestLogs'
      TargetFolder: '$(artifactsDir)\E2ETests\PackagedLog'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy E2E Test Source'
    inputs:
      SourceFolder: '$(Agent.TempDirectory)\TestLocalIndex'
      TargetFolder: '$(artifactsDir)\E2ETests\TestLocalIndex'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy TestData'
    inputs:
      SourceFolder: '$(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\'
      TargetFolder: '$(artifactsDir)\E2ETests\TestData'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy LocalhostWebServer'
    inputs:
      SourceFolder: '$(buildOutDir)\LocalhostWebServer'
      TargetFolder: '$(artifactsDir)\E2ETests\LocalhostWebServer'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy Dev Package Dependencies'
    inputs:
      SourceFolder: '$(appxPackageDir)\AppInstallerCLIPackage_0.0.2.0_Test\Dependencies\$(buildPlatform)\'
      TargetFolder: '$(artifactsDir)\E2ETests\DevPackageDependencies'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy test scripts to artifacts'
    inputs:
      Contents: |
          $(Build.SourcesDirectory)\src\PowerShell\scripts\Execute-WinGetTests.ps1
          $(Build.SourcesDirectory)\src\PowerShell\tests\**
          $(Build.SourcesDirectory)\src\LocalhostWebServer\Run-LocalhostWebServer.ps1
      TargetFolder: '$(artifactsDir)\E2ETests\Scripts'
      flattenFolders: true
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy Files: WinGetUtilInterop.UnitTests'
    inputs:
      SourceFolder: '$(Build.SourcesDirectory)\src\WinGetUtilInterop.UnitTests\bin\$(BuildConfiguration)\net6.0'
      TargetFolder:  '$(Build.ArtifactStagingDirectory)\WinGetUtilInterop.UnitTests\'
      CleanTargetFolder: true
      OverWrite: true
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy WinGetUtil to WinGetUtilInterop.UnitTests folder'
    inputs:
      Contents: |
          $(buildOutDir)\WinGetUtil\WinGetUtil.dll
      TargetFolder: '$(Build.ArtifactStagingDirectory)\WinGetUtilInterop.UnitTests\'
      flattenFolders: true
    condition: succeededOrFailed()

  - task: VSTest@2
    displayName: 'Run tests: WinGetUtilInterop.UnitTests'
    inputs:
      testSelector: 'testAssemblies'
      testAssemblyVer2: 'WinGetUtilInterop.UnitTests.dll'
      searchFolder: '$(Build.ArtifactStagingDirectory)\WinGetUtilInterop.UnitTests'
      codeCoverageEnabled: true
      platform: 'Any CPU'
      configuration: '$(BuildConfiguration)'
    condition: succeededOrFailed()
  
  - task: VSTest@2
    displayName: 'Run tests: Microsoft.Management.Configuration.UnitTests (InProc)'
    inputs:
      testRunTitle: Microsoft.Management.Configuration.UnitTests (InProc)
      testSelector: 'testAssemblies'
      testAssemblyVer2: '**\Microsoft.Management.Configuration.UnitTests.dll'
      searchFolder: '$(buildOutDir)\Microsoft.Management.Configuration.UnitTests'
      codeCoverageEnabled: true
      platform: '$(buildPlatform)'
      configuration: '$(BuildConfiguration)'
      diagnosticsEnabled: true
    condition: and(succeededOrFailed(), eq(variables.buildPlatform, 'x86'))

  - task: PowerShell@2
    displayName: Prepare for Microsoft.Management.Configuration.UnitTests (OutOfProc)
    inputs:
      filePath: 'src\Microsoft.Management.Configuration.OutOfProc\Prepare-ConfigurationOOPTests.ps1'
      arguments: '-BuildOutputPath $(buildOutDir) -PackageLayoutPath $(packageLayoutDir)'
    condition: succeededOrFailed()

  - task: VSTest@2
    displayName: 'Run tests: Microsoft.Management.Configuration.UnitTests (OutOfProc)'
    inputs:
      testRunTitle: Microsoft.Management.Configuration.UnitTests (OutOfProc)
      testSelector: 'testAssemblies'
      testAssemblyVer2: '**\Microsoft.Management.Configuration.UnitTests.dll'
      searchFolder: '$(buildOutDir)\Microsoft.Management.Configuration.UnitTests'
      testFiltercriteria: 'Category=OutOfProc'
      codeCoverageEnabled: true
      platform: '$(buildPlatform)'
      configuration: '$(BuildConfiguration)'
    condition: succeededOrFailed()

  - task: PowerShell@2
    displayName: Collect logs for Microsoft.Management.Configuration.UnitTests (OutOfProc)
    inputs:
      filePath: 'src\Microsoft.Management.Configuration.OutOfProc\Collect-ConfigurationOOPTests.ps1'
      arguments: '-TargetLocation $(artifactsDir)\ConfigOOPTestsLog'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy Util to artifacts folder'
    inputs:
      Contents: |
          $(buildOutDir)\WinGetUtil\WinGetUtil.dll
          $(buildOutDir)\WinGetUtil\WinGetUtil.pdb
      TargetFolder: '$(artifactsDir)'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy native binaries for Microsoft.WinGet.Client'
    inputs:
      SourceFolder: $(buildOutDir)
      Contents: |
          Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.dll
          Microsoft.Management.Deployment\Microsoft.Management.Deployment.winmd
          WindowsPackageManager\WindowsPackageManager.dll
          UndockedRegFreeWinRT\winrtact.dll
      TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Client\net6.0-windows10.0.22000.0\SharedDependencies\$(BuildPlatform)
      flattenFolders: true

  - task: CopyFiles@2
    displayName: 'Copy native binaries for Microsoft.WinGet.Client'
    inputs:
      SourceFolder: $(buildOutDir)
      Contents: |
          Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.dll
          Microsoft.Management.Deployment\Microsoft.Management.Deployment.winmd
          WindowsPackageManager\WindowsPackageManager.dll
          UndockedRegFreeWinRT\winrtact.dll
      TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Client\net48\SharedDependencies\$(BuildPlatform)
      flattenFolders: true

  - task: CopyFiles@2
    displayName: 'Copy native binaries for Microsoft.WinGet.Configuration'
    inputs:
      SourceFolder: $(buildOutDir)
      Contents: |
          Microsoft.Management.Configuration\Microsoft.Management.Configuration.dll
      TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Configuration\SharedDependencies\$(BuildPlatform)
      flattenFolders: true

  - task: CopyFiles@2
    displayName: 'Copy managed binaries for Microsoft.WinGet.Configuration in arch specific'
    inputs:
      SourceFolder: $(buildOutDirAnyCpu)
      Contents: |
          Microsoft.Management.Configuration.Projection\net6.0-windows10.0.19041.0\Microsoft.Management.Configuration.Projection.dll
      TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Configuration\SharedDependencies\$(BuildPlatform)
      flattenFolders: true

  - task: CopyFiles@2
    displayName: 'Copy PowerShell AnyCPU Module Files'
    inputs:
      SourceFolder: '$(buildOutDirAnyCpu)\PowerShell'
      TargetFolder: '$(artifactsDir)\PowerShell'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy Dev Package (Loose Files)'
    inputs:
      SourceFolder: '$(packageLayoutDir)'
      TargetFolder: '$(artifactsDir)\DevPackage'
    condition: succeededOrFailed()

  - task: CopyFiles@2
    displayName: 'Copy Dev Packages'
    inputs:
      SourceFolder: '$(appxPackageDir)'
      TargetFolder: '$(artifactsDir)\AppxPackages'
    condition: succeededOrFailed()

  - task: PublishPipelineArtifact@1
    displayName: Publish Pipeline Artifacts
    inputs:
      targetPath: '$(artifactsDir)'
    condition: succeededOrFailed()

  - task: ComponentGovernanceComponentDetection@0
    displayName: Component Governance
    inputs:
      scanType: 'Register'
      verbosity: 'Verbose'
      alertWarningLevel: 'High'

  # Run BimSkim for all the binaries
  - task: BinSkim@3
    displayName: 'Run BinSkim '
    inputs:
      arguments: 'analyze
        "$(buildOutDir)\AppInstallerCLI\winget.exe"
        "$(buildOutDir)\WinGetUtil\WinGetUtil.dll"
        "$(buildOutDir)\WindowsPackageManager\WindowsPackageManager.dll"
        "$(buildOutDir)\Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.InProc.dll"
        "$(Build.SourcesDirectory)\src\WinGetUtilInterop\bin\WinGetUtil*Interop.dll"
        "$(buildOutDir)\UndockedRegFreeWinRT\winrtact.dll"
        "$(buildOutDir)\Microsoft.WinGet.Client.Cmdlets\Microsoft.WinGet.Client*.dll"
        "$(buildOutDir)\ConfigurationRemotingServer\ConfigurationRemoting*Server.dll"
        "$(buildOutDir)\ConfigurationRemotingServer\ConfigurationRemoting*Server.exe"
        "$(buildOutDir)\ConfigurationRemotingServer\Microsoft.Management.Configuration*.dll"
        "$(buildOutDir)\Microsoft.Management.Configuration\Microsoft.Management.Configuration*.dll"
        "$(buildOutDir)\Microsoft.Management.Configuration.OutOfProc\Microsoft.Management.Configuration*.dll"
        --config default --recurse'

  - task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@2
    displayName: 'Publish Security Analysis Logs'

- job: 'BuildPowerShellModule'
  timeoutInMinutes: 120
  dependsOn: 'Build'
  condition: always()

  steps:
  - task: DownloadPipelineArtifact@2
    displayName: 'Download Build Artifacts'
  
  - task: CopyFiles@2
    displayName: 'Copy x64 PowerShell Binaries to Output'
    inputs:
      SourceFolder: '$(Pipeline.Workspace)\Build.x64release\PowerShell'
      Contents: '**\*'
      TargetFolder: '$(Build.ArtifactStagingDirectory)'

  - task: CopyFiles@2
    displayName: 'Copy x86 PowerShell Binaries to Output'
    inputs:
      SourceFolder: '$(Pipeline.Workspace)\Build.x86release\PowerShell'
      Contents: '**\*'
      TargetFolder: '$(Build.ArtifactStagingDirectory)'

  - task: CopyFiles@2
    displayName: 'Copy Microsoft.WinGet.DSC module to staging directory'
    inputs:
      SourceFolder: '$(Build.SourcesDirectory)\src\PowerShell\Microsoft.WinGet.DSC'
      Contents: '**\*'
      TargetFolder: '$(Build.ArtifactStagingDirectory)\Microsoft.WinGet.DSC'

  - task: PowerShell@2
    displayName: Install Tests Dependencies
    inputs:
      targetType: 'inline'
      script: |
        Get-ChildItem E2ETests\DevPackageDependencies -Filter *.appx | %{ Add-AppxPackage $_.FullName }
      workingDirectory: $(Pipeline.Workspace)\Build.x64release\

  - template: templates/e2e-setup.yml
    parameters:
      sourceDir: $(Build.SourcesDirectory)
      localhostWebServerArgs: '-BuildRoot $(Pipeline.Workspace)\Build.x64release\E2ETests\LocalhostWebServer -StaticFileRoot $(Pipeline.Workspace)\Build.x64release\E2ETests\TestLocalIndex -SourceCert $(Build.SourcesDirectory)\src\AppInstallerCLIE2ETests\TestData\AppInstallerTest.cer'

  - pwsh: .\RunTests.ps1 -testModulesPath $(Build.ArtifactStagingDirectory) -outputPath $(Pipeline.Workspace)\PesterTest -packageLayoutPath $(Pipeline.Workspace)\Build.x64release\DevPackage
    workingDirectory: $(Build.SourcesDirectory)\src\PowerShell\tests\
    displayName: Run PowerShell 7 Tests
  
  - powershell: .\RunTests.ps1 -testModulesPath $(Build.ArtifactStagingDirectory) -outputPath $(Pipeline.Workspace)\WPPesterTest
    workingDirectory: $(Build.SourcesDirectory)\src\PowerShell\tests\
    displayName: Run Windows PowerShell Tests
    condition: succeededOrFailed()

  - task: PublishTestResults@2
    displayName: Publish Pester Test Results PowerShell 7
    inputs:
      testResultsFormat: 'NUnit'
      testResultsFiles: '$(Pipeline.Workspace)\PesterTest\Test*.xml'
      failTaskOnFailedTests: true
    condition: succeededOrFailed()

  - task: PublishTestResults@2
    displayName: Publish Pester Test Results Windows PowerShell
    inputs:
      testResultsFormat: 'NUnit'
      testResultsFiles: '$(Pipeline.Workspace)\WPPesterTest\Test*.xml'
      failTaskOnFailedTests: true
    condition: succeededOrFailed()

  - task: PublishPipelineArtifact@1
    displayName: Publish PowerShell Module Artifacts
    inputs:
      targetPath: '$(Build.ArtifactStagingDirectory)'
    condition: succeededOrFailed()
