# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml

variables:
  outputFolder: './_output'
  artifactsFolder: './_artifacts'
  testsFolder: './_tests'
  yarnCacheFolder: $(Pipeline.Workspace)/.yarn
  nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
  majorVersion: '2.9.0'
  minorVersion: $[counter('minorVersion', 1076)]
  lidarrVersion: '$(majorVersion).$(minorVersion)'
  buildName: '$(Build.SourceBranchName).$(lidarrVersion)'
  sentryOrg: 'servarr'
  sentryUrl: 'https://sentry.servarr.com'
  dotnetVersion: '6.0.427'
  nodeVersion: '20.X'
  innoVersion: '6.2.0'
  windowsImage: 'windows-2022'
  linuxImage: 'ubuntu-20.04'
  macImage: 'macOS-13'

trigger:
  branches:
    include:
    - develop
    - master
  paths:
    exclude:
      - .github
      - src/Lidarr.Api.*/openapi.json

pr:
  branches:
    include:
    - develop
  paths:
    exclude:
    - .github
    - src/NzbDrone.Core/Localization/Core
    - src/Lidarr.Api.*/openapi.json

stages:
  - stage: Setup
    displayName: Setup
    jobs:
    - job:
      displayName: Build Variables
      pool:
        vmImage: ${{ variables.linuxImage }}
      steps:
      # Set the build name properly.  The 'name' property won't recursively expand so hack here:
      - bash: echo "##vso[build.updatebuildnumber]$LIDARRVERSION"
        displayName: Set Build Name
      - bash: |
          if [[ $BUILD_REASON == "PullRequest" ]]; then
          git diff origin/develop...HEAD  --name-only | grep -E "^(src/|azure-pipelines.yml)"
          echo $? > not_backend_update
          else
          echo 0 > not_backend_update
          fi
          cat not_backend_update
        displayName: Check for Backend File Changes
      - publish: not_backend_update
        artifact: not_backend_update
        displayName: Publish update type
  - stage: Build_Backend
    displayName: Build Backend
    dependsOn: Setup
    jobs:
    - job: Backend
      strategy:
        matrix:
          Linux:
            osName: 'Linux'
            imageName: ${{ variables.linuxImage }}
            enableAnalysis: 'true'
          Mac:
            osName: 'Mac'
            imageName: ${{ variables.macImage }}
            enableAnalysis: 'false'
          Windows:
            osName: 'Windows'
            imageName: ${{ variables.windowsImage }}
            enableAnalysis: 'false'

      pool:
        vmImage: $(imageName)
      variables:
        # Disable stylecop here - linting errors get caught by the analyze task
        EnableAnalyzers: $(enableAnalysis)
      steps:
      - checkout: self
        submodules: true
        fetchDepth: 1
      - task: UseDotNet@2
        displayName: 'Install .net core'
        inputs:
          version: $(dotnetVersion)
      - bash: |
          BUNDLEDVERSIONS=${AGENT_TOOLSDIRECTORY}/dotnet/sdk/${DOTNETVERSION}/Microsoft.NETCoreSdk.BundledVersions.props
          echo $BUNDLEDVERSIONS
          if grep -q freebsd-x64 $BUNDLEDVERSIONS; then
            echo "Extra platforms already enabled"
          else
            echo "Enabling extra platform support"
            sed -i.ORI 's/osx-x64/osx-x64;freebsd-x64;linux-x86/' $BUNDLEDVERSIONS
          fi
        displayName: Enable Extra Platform Support
      - bash: ./build.sh --backend --enable-extra-platforms
        displayName: Build Lidarr Backend
      - bash: |
          find ${OUTPUTFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
          find ${OUTPUTFOLDER} -depth -empty -type d -exec rm -r "{}" \;
          find ${TESTSFOLDER} -type f ! -path "*/publish/*" -exec rm -rf {} \;
          find ${TESTSFOLDER} -depth -empty -type d -exec rm -r "{}" \;
        displayName: Clean up intermediate output
        condition: and(succeeded(), ne(variables['osName'], 'Windows'))
      - publish: $(outputFolder)
        artifact: '$(osName)Backend'
        displayName: Publish Backend
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))
      - publish: '$(testsFolder)/net6.0/win-x64/publish'
        artifact: win-x64-tests
        displayName: Publish win-x64 Test Package
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))
      - publish: '$(testsFolder)/net6.0/linux-x64/publish'
        artifact: linux-x64-tests
        displayName: Publish linux-x64 Test Package
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))
      - publish: '$(testsFolder)/net6.0/linux-x86/publish'
        artifact: linux-x86-tests
        displayName: Publish linux-x86 Test Package
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))
      - publish: '$(testsFolder)/net6.0/linux-musl-x64/publish'
        artifact: linux-musl-x64-tests
        displayName: Publish linux-musl-x64 Test Package
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))
      - publish: '$(testsFolder)/net6.0/freebsd-x64/publish'
        artifact: freebsd-x64-tests
        displayName: Publish freebsd-x64 Test Package
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))
      - publish: '$(testsFolder)/net6.0/osx-x64/publish'
        artifact: osx-x64-tests
        displayName: Publish osx-x64 Test Package
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))

  - stage: Build_Frontend
    displayName: Frontend
    dependsOn: Setup
    jobs:
    - job: Build
      strategy:
        matrix:
          Linux:
            osName: 'Linux'
            imageName: ${{ variables.linuxImage }}
          Mac:
            osName: 'Mac'
            imageName: ${{ variables.macImage }}
          Windows:
            osName: 'Windows'
            imageName: ${{ variables.windowsImage }}
      pool:
        vmImage: $(imageName)
      steps:
      - task: UseNode@1
        displayName: Set Node.js version
        inputs:
          version: $(nodeVersion)
      - checkout: self
        submodules: true
        fetchDepth: 1
      - task: Cache@2
        inputs:
          key: 'yarn | "$(osName)" | yarn.lock'
          restoreKeys: |
             yarn | "$(osName)"
          path: $(yarnCacheFolder)
        displayName: Cache Yarn packages
      - bash: ./build.sh --frontend
        displayName: Build Lidarr Frontend
        env:
          FORCE_COLOR: 0
          YARN_CACHE_FOLDER: $(yarnCacheFolder)
      - publish: $(outputFolder)
        artifact: '$(osName)Frontend'
        displayName: Publish Frontend
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))

  - stage: Installer
    dependsOn:
    - Build_Backend
    - Build_Frontend
    jobs:
    - job: Windows_Installer
      displayName: Create Installer
      pool:
        vmImage: ${{ variables.windowsImage }}
      steps:
      - checkout: self
        fetchDepth: 1
      - task: DownloadPipelineArtifact@2
        inputs:
          buildType: 'current'
          artifactName: WindowsBackend
          targetPath: _output
        displayName: Fetch Backend
      - task: DownloadPipelineArtifact@2
        inputs:
          buildType: 'current'
          artifactName: WindowsFrontend
          targetPath: _output
        displayName: Fetch Frontend
      - bash: |
          ./build.sh --packages --installer
          cp distribution/windows/setup/output/Lidarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Lidarr.${BUILDNAME}.windows-core-x64-installer.exe
          cp distribution/windows/setup/output/Lidarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Lidarr.${BUILDNAME}.windows-core-x86-installer.exe
        displayName: Create Installers
      - publish: $(Build.ArtifactStagingDirectory)
        artifact: 'WindowsInstaller'
        displayName: Publish Installer

  - stage: Packages
    dependsOn:
    - Build_Backend
    - Build_Frontend
    jobs:
    - job: Other_Packages
      displayName: Create Standard Packages
      pool:
        vmImage: ${{ variables.linuxImage }}
      steps:
      - checkout: self
        fetchDepth: 1
      - task: DownloadPipelineArtifact@2
        inputs:
          buildType: 'current'
          artifactName: WindowsBackend
          targetPath: _output
        displayName: Fetch Backend
      - task: DownloadPipelineArtifact@2
        inputs:
          buildType: 'current'
          artifactName: WindowsFrontend
          targetPath: _output
        displayName: Fetch Frontend
      - bash: ./build.sh --packages --enable-extra-platforms
        displayName: Create Packages
      - bash: |
          find . -name "fpcalc" -exec chmod a+x {} \;
          find . -name "Lidarr" -exec chmod a+x {} \;
          find . -name "Lidarr.Update" -exec chmod a+x {} \;
        displayName: Set executable bits
      - task: ArchiveFiles@2
        displayName: Create win-x64 zip
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).windows-core-x64.zip'
          archiveType: 'zip'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/win-x64/net6.0
      - task: ArchiveFiles@2
        displayName: Create win-x86 zip
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).windows-core-x86.zip'
          archiveType: 'zip'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/win-x86/net6.0
      - task: ArchiveFiles@2
        displayName: Create osx-x64 app
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).osx-app-core-x64.zip'
          archiveType: 'zip'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/osx-x64-app/net6.0
      - task: ArchiveFiles@2
        displayName: Create osx-x64 tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).osx-core-x64.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/osx-x64/net6.0
      - task: ArchiveFiles@2
        displayName: Create osx-arm64 app
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).osx-app-core-arm64.zip'
          archiveType: 'zip'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/osx-arm64-app/net6.0
      - task: ArchiveFiles@2
        displayName: Create osx-arm64 tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).osx-core-arm64.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/osx-arm64/net6.0
      - task: ArchiveFiles@2
        displayName: Create linux-x64 tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).linux-core-x64.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/linux-x64/net6.0
      - task: ArchiveFiles@2
        displayName: Create linux-musl-x64 tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).linux-musl-core-x64.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/linux-musl-x64/net6.0
      - task: ArchiveFiles@2
        displayName: Create linux-x86 tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).linux-core-x86.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/linux-x86/net6.0
      - task: ArchiveFiles@2
        displayName: Create linux-arm tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).linux-core-arm.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/linux-arm/net6.0
      - task: ArchiveFiles@2
        displayName: Create linux-musl-arm tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).linux-musl-core-arm.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/linux-musl-arm/net6.0
      - task: ArchiveFiles@2
        displayName: Create linux-arm64 tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).linux-core-arm64.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/linux-arm64/net6.0
      - task: ArchiveFiles@2
        displayName: Create linux-musl-arm64 tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).linux-musl-core-arm64.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/linux-musl-arm64/net6.0
      - task: ArchiveFiles@2
        displayName: Create freebsd-x64 tar
        inputs:
          archiveFile: '$(Build.ArtifactStagingDirectory)/Lidarr.$(buildName).freebsd-core-x64.tar.gz'
          archiveType: 'tar'
          tarCompression: 'gz'
          includeRootFolder: false
          rootFolderOrFile: $(artifactsFolder)/freebsd-x64/net6.0
      - publish: $(Build.ArtifactStagingDirectory)
        artifact: 'Packages'
        displayName: Publish Packages
      - bash: |
          echo "Uploading source maps to sentry"
          curl -sL https://sentry.io/get-cli/ | bash
          RELEASENAME="Lidarr@${LIDARRVERSION}-${BUILD_SOURCEBRANCHNAME}"
          sentry-cli releases new --finalize -p lidarr -p lidarr-ui -p lidarr-update "${RELEASENAME}"
          sentry-cli releases -p lidarr-ui files "${RELEASENAME}" upload-sourcemaps _output/UI/ --rewrite
          sentry-cli releases set-commits --auto "${RELEASENAME}"
          if [[ ${BUILD_SOURCEBRANCH} == "refs/heads/develop" ]]; then
          sentry-cli releases deploys "${RELEASENAME}" new -e nightly
          else
          sentry-cli releases deploys "${RELEASENAME}" new -e production
          fi
          if [ $? -gt 0 ]; then
            echo "##vso[task.logissue type=warning]Error uploading source maps."
          fi
          exit 0
        displayName: Publish Sentry Source Maps
        condition: |
          or
          (
            and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
            and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
          )
        env:
          SENTRY_AUTH_TOKEN: $(sentryAuthTokenServarr)
          SENTRY_ORG: $(sentryOrg)
          SENTRY_URL: $(sentryUrl)

  - stage: Unit_Test
    displayName: Unit Tests
    dependsOn: Build_Backend
    condition: succeeded()

    jobs:
    - job: Prepare
      pool:
        vmImage: ${{ variables.linuxImage }}
      steps:
      - checkout: none
      - task: DownloadPipelineArtifact@2
        inputs:
          buildType: 'current'
          artifactName: 'not_backend_update'
          targetPath: '.'
      - bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
        name: setVar

    - job: Unit
      displayName: Unit Native
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      workspace:
        clean: all

      strategy:
        matrix:
          MacCore:
            osName: 'Mac'
            testName: 'osx-x64'
            poolName: 'Azure Pipelines'
            imageName: ${{ variables.macImage }}
          WindowsCore:
            osName: 'Windows'
            testName: 'win-x64'
            poolName: 'Azure Pipelines'
            imageName: ${{ variables.windowsImage }}
          LinuxCore:
            osName: 'Linux'
            testName: 'linux-x64'
            poolName: 'Azure Pipelines'
            imageName: ${{ variables.linuxImage }}
          FreebsdCore:
            osName: 'Linux'
            testName: 'freebsd-x64'
            poolName: 'FreeBSD'
            imageName:

      pool:
        name: $(poolName)
        vmImage: $(imageName)

      steps:
      - checkout: none
      - task: UseDotNet@2
        displayName: 'Install .net core'
        inputs:
          version: $(dotnetVersion)
        condition: ne(variables['poolName'], 'FreeBSD')
      - task: DownloadPipelineArtifact@2
        displayName: Download Test Artifact
        inputs:
          buildType: 'current'
          artifactName: '$(testName)-tests'
          targetPath: $(testsFolder)
      - powershell: Set-Service SCardSvr -StartupType Manual
        displayName: Enable Windows Test Service
        condition: and(succeeded(), eq(variables['osName'], 'Windows'))
      - bash: |
          chmod a+x _tests/fpcalc
        displayName: Make fpcalc Executable
        condition: and(succeeded(), and(ne(variables['osName'], 'Windows'), ne(variables['poolName'], 'FreeBSD')))
      - bash: find ${TESTSFOLDER} -name "Lidarr.Test.Dummy" -exec chmod a+x {} \;
        displayName: Make Test Dummy Executable
        condition: and(succeeded(), ne(variables['osName'], 'Windows'))
      - bash: |
          chmod a+x ${TESTSFOLDER}/test.sh
          ${TESTSFOLDER}/test.sh ${OSNAME} Unit Test
        displayName: Run Tests
        env:
          TEST_DIR: $(Build.SourcesDirectory)/_tests
      - task: PublishTestResults@2
        displayName: Publish Test Results
        inputs:
          testResultsFormat: 'NUnit'
          testResultsFiles: '**/TestResult.xml'
          testRunTitle: '$(testName) Unit Tests'
          failTaskOnFailedTests: true

    - job: Unit_Docker
      displayName: Unit Docker
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      strategy:
        matrix:
          alpine:
            testName: 'Musl Net Core'
            artifactName: linux-musl-x64-tests
            containerImage: ghcr.io/servarr/testimages:alpine
          linux-x86:
            testName: 'linux-x86'
            artifactName: linux-x86-tests
            containerImage: ghcr.io/servarr/testimages:linux-x86

      pool:
        vmImage: ${{ variables.linuxImage }}

      container: $[ variables['containerImage'] ]

      timeoutInMinutes: 10

      steps:
      - task: UseDotNet@2
        displayName: 'Install .NET'
        inputs:
          version: $(dotnetVersion)
        condition: and(succeeded(), ne(variables['testName'], 'linux-x86'))
      - bash: |
          SDKURL=$(curl -s https://api.github.com/repos/Servarr/dotnet-linux-x86/releases | jq -rc '.[].assets[].browser_download_url' | grep sdk-${DOTNETVERSION}.*gz$)
          curl -fsSL $SDKURL | tar xzf - -C /opt/dotnet
        displayName: 'Install .NET'
        condition: and(succeeded(), eq(variables['testName'], 'linux-x86'))
      - checkout: none
      - task: DownloadPipelineArtifact@2
        displayName: Download Test Artifact
        inputs:
          buildType: 'current'
          artifactName: $(artifactName)
          targetPath: $(testsFolder)
      - bash: |
          chmod a+x _tests/fpcalc
        displayName: Make fpcalc Executable
        condition: and(succeeded(), ne(variables['osName'], 'Windows'))
      - bash: find ${TESTSFOLDER} -name "Lidarr.Test.Dummy" -exec chmod a+x {} \;
        displayName: Make Test Dummy Executable
        condition: and(succeeded(), ne(variables['osName'], 'Windows'))
      - bash: |
          chmod a+x ${TESTSFOLDER}/test.sh
          ls -lR ${TESTSFOLDER}
          ${TESTSFOLDER}/test.sh Linux Unit Test
        displayName: Run Tests
      - task: PublishTestResults@2
        displayName: Publish Test Results
        inputs:
          testResultsFormat: 'NUnit'
          testResultsFiles: '**/TestResult.xml'
          testRunTitle: '$(testName) Unit Tests'
          failTaskOnFailedTests: true

    - job: Unit_LinuxCore_Postgres14
      displayName: Unit Native LinuxCore with Postgres14 Database
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      variables:
        pattern: 'Lidarr.*.linux-core-x64.tar.gz'
        artifactName: linux-x64-tests
        Lidarr__Postgres__Host: 'localhost'
        Lidarr__Postgres__Port: '5432'
        Lidarr__Postgres__User: 'lidarr'
        Lidarr__Postgres__Password: 'lidarr'

      pool:
        vmImage: ${{ variables.linuxImage }}

      timeoutInMinutes: 10

      steps:
      - task: UseDotNet@2
        displayName: 'Install .net core'
        inputs:
          version: $(dotnetVersion)
      - checkout: none
      - task: DownloadPipelineArtifact@2
        displayName: Download Test Artifact
        inputs:
          buildType: 'current'
          artifactName: $(artifactName)
          targetPath: $(testsFolder)
      - bash: |
          chmod a+x _tests/fpcalc
        displayName: Make fpcalc Executable
        condition: and(succeeded(), ne(variables['osName'], 'Windows'))
      - bash: find ${TESTSFOLDER} -name "Lidarr.Test.Dummy" -exec chmod a+x {} \;
        displayName: Make Test Dummy Executable
        condition: and(succeeded(), ne(variables['osName'], 'Windows'))
      - bash: |
          docker run -d --name=postgres14 \
          -e POSTGRES_PASSWORD=lidarr \
          -e POSTGRES_USER=lidarr \
          -p 5432:5432/tcp \
          -v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
          postgres:14
        displayName: Start postgres
      - bash: |
          chmod a+x ${TESTSFOLDER}/test.sh
          ls -lR ${TESTSFOLDER}
          ${TESTSFOLDER}/test.sh Linux Unit Test
        displayName: Run Tests
      - task: PublishTestResults@2
        displayName: Publish Test Results
        inputs:
          testResultsFormat: 'NUnit'
          testResultsFiles: '**/TestResult.xml'
          testRunTitle: 'LinuxCore Postgres14 Unit Tests'
          failTaskOnFailedTests: true

    - job: Unit_LinuxCore_Postgres15
      displayName: Unit Native LinuxCore with Postgres15 Database
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      variables:
        pattern: 'Lidarr.*.linux-core-x64.tar.gz'
        artifactName: linux-x64-tests
        Lidarr__Postgres__Host: 'localhost'
        Lidarr__Postgres__Port: '5432'
        Lidarr__Postgres__User: 'lidarr'
        Lidarr__Postgres__Password: 'lidarr'

      pool:
        vmImage: ${{ variables.linuxImage }}

      timeoutInMinutes: 10

      steps:
      - task: UseDotNet@2
        displayName: 'Install .net core'
        inputs:
          version: $(dotnetVersion)
      - checkout: none
      - task: DownloadPipelineArtifact@2
        displayName: Download Test Artifact
        inputs:
          buildType: 'current'
          artifactName: $(artifactName)
          targetPath: $(testsFolder)
      - bash: |
          chmod a+x _tests/fpcalc
        displayName: Make fpcalc Executable
        condition: and(succeeded(), ne(variables['osName'], 'Windows'))
      - bash: find ${TESTSFOLDER} -name "Lidarr.Test.Dummy" -exec chmod a+x {} \;
        displayName: Make Test Dummy Executable
        condition: and(succeeded(), ne(variables['osName'], 'Windows'))
      - bash: |
          docker run -d --name=postgres15 \
          -e POSTGRES_PASSWORD=lidarr \
          -e POSTGRES_USER=lidarr \
          -p 5432:5432/tcp \
          -v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
          postgres:15
        displayName: Start postgres
      - bash: |
          chmod a+x ${TESTSFOLDER}/test.sh
          ls -lR ${TESTSFOLDER}
          ${TESTSFOLDER}/test.sh Linux Unit Test
        displayName: Run Tests
      - task: PublishTestResults@2
        displayName: Publish Test Results
        inputs:
          testResultsFormat: 'NUnit'
          testResultsFiles: '**/TestResult.xml'
          testRunTitle: 'LinuxCore Postgres15 Unit Tests'
          failTaskOnFailedTests: true

  - stage: Integration
    displayName: Integration
    dependsOn: Packages

    jobs:
    - job: Prepare
      pool:
        vmImage: ${{ variables.linuxImage }}
      steps:
      - checkout: none
      - task: DownloadPipelineArtifact@2
        inputs:
          buildType: 'current'
          artifactName: 'not_backend_update'
          targetPath: '.'
      - bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
        name: setVar

    - job: Integration_Native
      displayName: Integration Native
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      strategy:
        matrix:
          MacCore:
            osName: 'Mac'
            testName: 'osx-x64'
            imageName: ${{ variables.macImage }}
            pattern: 'Lidarr.*.osx-core-x64.tar.gz'
          WindowsCore:
            osName: 'Windows'
            testName: 'win-x64'
            imageName: ${{ variables.windowsImage }}
            pattern: 'Lidarr.*.windows-core-x64.zip'
          LinuxCore:
            osName: 'Linux'
            testName: 'linux-x64'
            imageName: ${{ variables.linuxImage }}
            pattern: 'Lidarr.*.linux-core-x64.tar.gz'

      pool:
        vmImage: $(imageName)

      steps:
      - task: UseDotNet@2
        displayName: 'Install .net core'
        inputs:
          version: $(dotnetVersion)
      - checkout: none
      - task: DownloadPipelineArtifact@2
        displayName: Download Test Artifact
        inputs:
          buildType: 'current'
          artifactName: '$(testName)-tests'
          targetPath: $(testsFolder)
      - task: DownloadPipelineArtifact@2
        displayName: Download Build Artifact
        inputs:
          buildType: 'current'
          artifactName: Packages
          itemPattern: '**/$(pattern)'
          targetPath: $(Build.ArtifactStagingDirectory)
      - task: ExtractFiles@1
        inputs:
          archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
          destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
        displayName: Extract Package
      - bash: |
          mkdir -p ./bin/
          cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Lidarr/. ./bin/
        displayName: Move Package Contents
      - bash: |
          chmod a+x ${TESTSFOLDER}/test.sh
          ${TESTSFOLDER}/test.sh ${OSNAME} Integration Test
        displayName: Run Integration Tests
      - task: PublishTestResults@2
        inputs:
          testResultsFormat: 'NUnit'
          testResultsFiles: '**/TestResult.xml'
          testRunTitle: '$(testName) Integration Tests'
          failTaskOnFailedTests: true
        displayName: Publish Test Results

    - job: Integration_LinuxCore_Postgres14
      displayName: Integration Native LinuxCore with Postgres14 Database
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      variables:
        pattern: 'Lidarr.*.linux-core-x64.tar.gz'
        Lidarr__Postgres__Host: 'localhost'
        Lidarr__Postgres__Port: '5432'
        Lidarr__Postgres__User: 'lidarr'
        Lidarr__Postgres__Password: 'lidarr'

      pool:
        vmImage: ${{ variables.linuxImage }}

      steps:
        - task: UseDotNet@2
          displayName: 'Install .net core'
          inputs:
            version: $(dotnetVersion)
        - checkout: none
        - task: DownloadPipelineArtifact@2
          displayName: Download Test Artifact
          inputs:
            buildType: 'current'
            artifactName: 'linux-x64-tests'
            targetPath: $(testsFolder)
        - task: DownloadPipelineArtifact@2
          displayName: Download Build Artifact
          inputs:
            buildType: 'current'
            artifactName: Packages
            itemPattern: '**/$(pattern)'
            targetPath: $(Build.ArtifactStagingDirectory)
        - task: ExtractFiles@1
          inputs:
            archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
            destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
          displayName: Extract Package
        - bash: |
            mkdir -p ./bin/
            cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Lidarr/. ./bin/
          displayName: Move Package Contents
        - bash: |
            docker run -d --name=postgres14 \
            -e POSTGRES_PASSWORD=lidarr \
            -e POSTGRES_USER=lidarr \
            -p 5432:5432/tcp \
            -v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
            postgres:14
          displayName: Start postgres
        - bash: |
            chmod a+x ${TESTSFOLDER}/test.sh
            ${TESTSFOLDER}/test.sh Linux Integration Test
          displayName: Run Integration Tests
        - task: PublishTestResults@2
          inputs:
            testResultsFormat: 'NUnit'
            testResultsFiles: '**/TestResult.xml'
            testRunTitle: 'Integration LinuxCore Postgres14 Database Integration Tests'
            failTaskOnFailedTests: true
          displayName: Publish Test Results


    - job: Integration_LinuxCore_Postgres15
      displayName: Integration Native LinuxCore with Postgres Database
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      variables:
        pattern: 'Lidarr.*.linux-core-x64.tar.gz'
        Lidarr__Postgres__Host: 'localhost'
        Lidarr__Postgres__Port: '5432'
        Lidarr__Postgres__User: 'lidarr'
        Lidarr__Postgres__Password: 'lidarr'

      pool:
        vmImage: ${{ variables.linuxImage }}

      steps:
        - task: UseDotNet@2
          displayName: 'Install .net core'
          inputs:
            version: $(dotnetVersion)
        - checkout: none
        - task: DownloadPipelineArtifact@2
          displayName: Download Test Artifact
          inputs:
            buildType: 'current'
            artifactName: 'linux-x64-tests'
            targetPath: $(testsFolder)
        - task: DownloadPipelineArtifact@2
          displayName: Download Build Artifact
          inputs:
            buildType: 'current'
            artifactName: Packages
            itemPattern: '**/$(pattern)'
            targetPath: $(Build.ArtifactStagingDirectory)
        - task: ExtractFiles@1
          inputs:
            archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
            destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
          displayName: Extract Package
        - bash: |
            mkdir -p ./bin/
            cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Lidarr/. ./bin/
          displayName: Move Package Contents
        - bash: |
            docker run -d --name=postgres15 \
            -e POSTGRES_PASSWORD=lidarr \
            -e POSTGRES_USER=lidarr \
            -p 5432:5432/tcp \
            -v /usr/share/zoneinfo/America/Chicago:/etc/localtime:ro \
            postgres:15
          displayName: Start postgres
        - bash: |
            chmod a+x ${TESTSFOLDER}/test.sh
            ${TESTSFOLDER}/test.sh Linux Integration Test
          displayName: Run Integration Tests
        - task: PublishTestResults@2
          inputs:
            testResultsFormat: 'NUnit'
            testResultsFiles: '**/TestResult.xml'
            testRunTitle: 'Integration LinuxCore Postgres15 Database Integration Tests'
            failTaskOnFailedTests: true
          displayName: Publish Test Results

    - job: Integration_FreeBSD
      displayName: Integration Native FreeBSD
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      workspace:
        clean: all
      variables:
        pattern: 'Lidarr.*.freebsd-core-x64.tar.gz'
      pool:
        name: 'FreeBSD'

      steps:
      - checkout: none
      - task: DownloadPipelineArtifact@2
        displayName: Download Test Artifact
        inputs:
          buildType: 'current'
          artifactName: 'freebsd-x64-tests'
          targetPath: $(testsFolder)
      - task: DownloadPipelineArtifact@2
        displayName: Download Build Artifact
        inputs:
          buildType: 'current'
          artifactName: Packages
          itemPattern: '**/$(pattern)'
          targetPath: $(Build.ArtifactStagingDirectory)
      - bash: |
          mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
          tar xf ${BUILD_ARTIFACTSTAGINGDIRECTORY}/$(pattern) -C ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
        displayName: Extract Package
      - bash: |
          mkdir -p ./bin/
          cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Lidarr/. ./bin/
        displayName: Move Package Contents
      - bash: |
          chmod a+x ${TESTSFOLDER}/test.sh
          ${TESTSFOLDER}/test.sh Linux Integration Test
        displayName: Run Integration Tests
      - task: PublishTestResults@2
        inputs:
          testResultsFormat: 'NUnit'
          testResultsFiles: '**/TestResult.xml'
          testRunTitle: 'FreeBSD Integration Tests'
          failTaskOnFailedTests: true
        displayName: Publish Test Results

    - job: Integration_Docker
      displayName: Integration Docker
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
      strategy:
        matrix:
          alpine:
            testName: 'linux-musl-x64'
            artifactName: linux-musl-x64-tests
            containerImage: ghcr.io/servarr/testimages:alpine
            pattern: 'Lidarr.*.linux-musl-core-x64.tar.gz'
          linux-x86:
            testName: 'linux-x86'
            artifactName: linux-x86-tests
            containerImage: ghcr.io/servarr/testimages:linux-x86
            pattern: 'Lidarr.*.linux-core-x86.tar.gz'
      pool:
        vmImage: ${{ variables.linuxImage }}

      container: $[ variables['containerImage'] ]

      timeoutInMinutes: 15

      steps:
      - task: UseDotNet@2
        displayName: 'Install .NET'
        inputs:
          version: $(dotnetVersion)
        condition: and(succeeded(), ne(variables['testName'], 'linux-x86'))
      - bash: |
          SDKURL=$(curl -s https://api.github.com/repos/Servarr/dotnet-linux-x86/releases | jq -rc '.[].assets[].browser_download_url' | grep sdk-${DOTNETVERSION}.*gz$)
          curl -fsSL $SDKURL | tar xzf - -C /opt/dotnet
        displayName: 'Install .NET'
        condition: and(succeeded(), eq(variables['testName'], 'linux-x86'))
      - checkout: none
      - task: DownloadPipelineArtifact@2
        displayName: Download Test Artifact
        inputs:
          buildType: 'current'
          artifactName: $(artifactName)
          targetPath: $(testsFolder)
      - task: DownloadPipelineArtifact@2
        displayName: Download Build Artifact
        inputs:
          buildType: 'current'
          artifactName: Packages
          itemPattern: '**/$(pattern)'
          targetPath: $(Build.ArtifactStagingDirectory)
      - task: ExtractFiles@1
        inputs:
          archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
          destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
        displayName: Extract Package
      - bash: |
          mkdir -p ./bin/
          cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Lidarr/. ./bin/
        displayName: Move Package Contents
      - bash: |
          chmod a+x ${TESTSFOLDER}/test.sh
          ${TESTSFOLDER}/test.sh Linux Integration Test
        displayName: Run Integration Tests
      - task: PublishTestResults@2
        inputs:
          testResultsFormat: 'NUnit'
          testResultsFiles: '**/TestResult.xml'
          testRunTitle: '$(testName) Integration Tests'
          failTaskOnFailedTests: true
        displayName: Publish Test Results

  - stage: Automation
    displayName: Automation
    dependsOn: Packages

    jobs:
    - job: Automation
      strategy:
        matrix:
          Linux:
            osName: 'Linux'
            artifactName: 'linux-x64'
            imageName: ${{ variables.linuxImage }}
            pattern: 'Lidarr.*.linux-core-x64.tar.gz'
            failBuild: true
          Mac:
            osName: 'Mac'
            artifactName: 'osx-x64'
            imageName: ${{ variables.macImage }}
            pattern: 'Lidarr.*.osx-core-x64.tar.gz'
            failBuild: true
          Windows:
            osName: 'Windows'
            artifactName: 'win-x64'
            imageName: ${{ variables.windowsImage }}
            pattern: 'Lidarr.*.windows-core-x64.zip'
            failBuild: true

      pool:
        vmImage: $(imageName)

      steps:
      - task: UseDotNet@2
        displayName: 'Install .net core'
        inputs:
          version: $(dotnetVersion)
      - checkout: none
      - task: DownloadPipelineArtifact@2
        displayName: Download Test Artifact
        inputs:
          buildType: 'current'
          artifactName: '$(artifactName)-tests'
          targetPath: $(testsFolder)
      - task: DownloadPipelineArtifact@2
        displayName: Download Build Artifact
        inputs:
          buildType: 'current'
          artifactName: Packages
          itemPattern: '**/$(pattern)'
          targetPath: $(Build.ArtifactStagingDirectory)
      - task: ExtractFiles@1
        inputs:
          archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/**/$(pattern)'
          destinationFolder: '$(Build.ArtifactStagingDirectory)/bin'
        displayName: Extract Package
      - bash: |
          mkdir -p ./bin/
          cp -r -v ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin/Lidarr/. ./bin/
        displayName: Move Package Contents
      - bash: |
          chmod a+x ${TESTSFOLDER}/test.sh
          ${TESTSFOLDER}/test.sh ${OSNAME} Automation Test
        displayName: Run Automation Tests
      - task: CopyFiles@2
        displayName: 'Copy Screenshot to: $(Build.ArtifactStagingDirectory)'
        inputs:
          SourceFolder: '$(Build.SourcesDirectory)'
          Contents: |
            **/*_test_screenshot.png
          TargetFolder: '$(Build.ArtifactStagingDirectory)/screenshots'
      - publish: $(Build.ArtifactStagingDirectory)/screenshots
        artifact: '$(osName)AutomationScreenshots'
        displayName: Publish Screenshot Bundle
        condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))
      - task: PublishTestResults@2
        inputs:
          testResultsFormat: 'NUnit'
          testResultsFiles: '**/TestResult.xml'
          testRunTitle: '$(osName) Automation Tests'
          failTaskOnFailedTests: $(failBuild)
        displayName: Publish Test Results

  - stage: Analyze
    dependsOn:
    - Setup
    displayName: Analyze

    jobs:
    - job: Prepare
      pool:
        vmImage: ${{ variables.linuxImage }}
      steps:
      - checkout: none
      - task: DownloadPipelineArtifact@2
        inputs:
          buildType: 'current'
          artifactName: 'not_backend_update'
          targetPath: '.'
      - bash: echo "##vso[task.setvariable variable=backendNotUpdated;isOutput=true]$(cat not_backend_update)"
        name: setVar

    - job: Lint_Frontend
      displayName: Lint Frontend
      strategy:
        matrix:
          Linux:
            osName: 'Linux'
            imageName: ${{ variables.linuxImage }}
          Windows:
            osName: 'Windows'
            imageName: ${{ variables.windowsImage }}
      pool:
        vmImage: $(imageName)
      steps:
      - task: UseNode@1
        displayName: Set Node.js version
        inputs:
          version: $(nodeVersion)
      - checkout: self
        submodules: true
        fetchDepth: 1
      - task: Cache@2
        inputs:
          key: 'yarn | "$(osName)" | yarn.lock'
          restoreKeys: |
             yarn | "$(osName)"
          path: $(yarnCacheFolder)
        displayName: Cache Yarn packages
      - bash: ./build.sh --lint
        displayName: Lint Lidarr Frontend
        env:
          FORCE_COLOR: 0
          YARN_CACHE_FOLDER: $(yarnCacheFolder)

    - job: Analyze_Frontend
      displayName: Frontend
      condition: eq(variables['System.PullRequest.IsFork'], 'False')
      pool:
        vmImage: ${{ variables.windowsImage }}
      steps:
      - checkout: self # Need history for Sonar analysis
      - task: SonarCloudPrepare@2
        env:
          SONAR_SCANNER_OPTS: ''
        inputs:
          SonarCloud: 'SonarCloud'
          organization: 'lidarr'
          scannerMode: 'CLI'
          configMode: 'manual'
          cliProjectKey: 'lidarr_Lidarr.UI'
          cliProjectName: 'LidarrUI'
          cliProjectVersion: '$(lidarrVersion)'
          cliSources: './frontend'
      - task: SonarCloudAnalyze@2

    - job: Api_Docs
      displayName: API Docs
      dependsOn: Prepare
      condition: |
        and
        (
          and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop')),
          and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))
        )

      pool:
        vmImage: ${{ variables.windowsImage }}

      steps:
      - task: UseDotNet@2
        displayName: 'Install .net core'
        inputs:
          version: $(dotnetVersion)
      - checkout: self
        submodules: true
        persistCredentials: true
        fetchDepth: 1
      - bash: ./docs.sh Windows
        displayName: Create openapi.json
      - bash: |
          git config --global user.email "development@lidarr.audio"
          git config --global user.name "Servarr"
          git checkout -b api-docs
          git add .
          if git status | grep -q modified
          then
            git commit -am 'Automated API Docs update'
            git push -f --set-upstream origin api-docs
            curl -X POST -H "Authorization: token ${GITHUBTOKEN}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/lidarr/lidarr/pulls -d '{"head":"api-docs","base":"develop","title":"Update API docs"}'
          else
            echo "No changes since last run"
          fi
        displayName: Commit API Doc Change
        continueOnError: true
        env:
          GITHUBTOKEN: $(githubToken)
      - task: CopyFiles@2
        displayName: 'Copy openapi.json to: $(Build.ArtifactStagingDirectory)'
        inputs:
          SourceFolder: '$(Build.SourcesDirectory)'
          Contents: |
            **/*openapi.json
          TargetFolder: '$(Build.ArtifactStagingDirectory)/api_docs'
      - publish: $(Build.ArtifactStagingDirectory)/api_docs
        artifact: 'APIDocs'
        displayName: Publish API Docs Bundle
        condition: and(succeeded(), eq(variables['System.JobAttempt'], '1'))

    - job: Analyze_Backend
      displayName: Backend
      dependsOn: Prepare
      condition: and(succeeded(), eq(dependencies.Prepare.outputs['setVar.backendNotUpdated'], '0'))

      variables:
        disable.coverage.autogenerate: 'true'
        EnableAnalyzers: 'false'

      pool:
        vmImage: ${{ variables.windowsImage }}

      steps:
      - task: UseDotNet@2
        displayName: 'Install .net core'
        inputs:
          version: $(dotnetVersion)
      - checkout: self # Need history for Sonar analysis
        submodules: true
      - powershell: Set-Service SCardSvr -StartupType Manual
        displayName: Enable Windows Test Service
      - task: SonarCloudPrepare@2
        condition: eq(variables['System.PullRequest.IsFork'], 'False')
        inputs:
          SonarCloud: 'SonarCloud'
          organization: 'lidarr'
          scannerMode: 'MSBuild'
          projectKey: 'lidarr_Lidarr'
          projectName: 'Lidarr'
          projectVersion: '$(lidarrVersion)'
          extraProperties: |
            sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/**
            sonar.coverage.exclusions=**/Lidarr.Api.V1/**/*
            sonar.cs.opencover.reportsPaths=$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml
            sonar.cs.nunit.reportsPaths=$(Build.SourcesDirectory)/TestResult.xml
      - bash: |
          ./build.sh --backend -f net6.0 -r win-x64
          TEST_DIR=_tests/net6.0/win-x64/publish/ ./test.sh Windows Unit Coverage
        displayName: Coverage Unit Tests
      - task: SonarCloudAnalyze@2
        condition: eq(variables['System.PullRequest.IsFork'], 'False')
        displayName: Publish SonarCloud Results
      - task: reportgenerator@5.3.11
        displayName: Generate Coverage Report
        inputs:
          reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml'
          targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined'
          reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
          publishCodeCoverageResults: true

  - stage: Report_Out
    dependsOn:
    - Analyze
    - Unit_Test
    - Integration
    - Automation
    condition: eq(variables['system.pullrequest.isfork'], false)
    displayName: Build Status Report
    jobs:
    - job:
      displayName: Discord Notification
      pool:
        vmImage: ${{ variables.linuxImage }}
      steps:
        - task: DownloadPipelineArtifact@2
          continueOnError: true
          displayName: Download Screenshot Artifact
          inputs:
            buildType: 'current'
            artifactName: 'WindowsAutomationScreenshots'
            targetPath: $(Build.SourcesDirectory)
        - checkout: none
        - pwsh: |
            iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/Servarr/AzureDiscordNotify/master/DiscordNotify.ps1'))
          env:
            SYSTEM_ACCESSTOKEN: $(System.AccessToken)
            DISCORDCHANNELID: $(discordChannelId)
            DISCORDWEBHOOKKEY: $(discordWebhookKey)
            DISCORDTHREADID: $(discordThreadId)