# 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: '0.3.15' minorVersion: $[counter('minorVersion', 1)] readarrVersion: '$(majorVersion).$(minorVersion)' buildName: '$(Build.SourceBranchName).$(readarrVersion)' sentryOrg: 'servarr' sentryUrl: 'https://sentry.servarr.com' dotnetVersion: '6.0.417' nodeVersion: '16.X' innoVersion: '6.2.0' windowsImage: 'windows-2022' linuxImage: 'ubuntu-20.04' macImage: 'macOS-11' trigger: branches: include: - develop - master paths: exclude: - .github - src/Readarr.Api.*/openapi.json pr: branches: include: - develop paths: exclude: - .github - src/NzbDrone.Core/Localization/Core - src/Readarr.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]$READARRVERSION" 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 Readarr 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: NodeTool@0 displayName: Set Node.js version inputs: versionSpec: $(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 Readarr 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/Readarr.*win-x64.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${BUILDNAME}.windows-core-x64-installer.exe cp distribution/windows/setup/output/Readarr.*win-x86.exe ${BUILD_ARTIFACTSTAGINGDIRECTORY}/Readarr.${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 "Readarr" -exec chmod a+x {} \; find . -name "Readarr.Update" -exec chmod a+x {} \; displayName: Set executable bits - task: ArchiveFiles@2 displayName: Create win-x64 zip inputs: archiveFile: '$(Build.ArtifactStagingDirectory)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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)/Readarr.$(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="Readarr@${READARRVERSION}-${BUILD_SOURCEBRANCHNAME}" sentry-cli releases new --finalize -p readarr -p readarr-ui -p readarr-update "${RELEASENAME}" sentry-cli releases -p readarr-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 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: find ${TESTSFOLDER} -name "Readarr.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: find ${TESTSFOLDER} -name "Readarr.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: 'Readarr.*.linux-core-x64.tar.gz' artifactName: linux-x64-tests Readarr__Postgres__Host: 'localhost' Readarr__Postgres__Port: '5432' Readarr__Postgres__User: 'readarr' Readarr__Postgres__Password: 'readarr' 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: find ${TESTSFOLDER} -name "Readarr.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=readarr \ -e POSTGRES_USER=readarr \ -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: 'Readarr.*.linux-core-x64.tar.gz' artifactName: linux-x64-tests Readarr__Postgres__Host: 'localhost' Readarr__Postgres__Port: '5432' Readarr__Postgres__User: 'readarr' Readarr__Postgres__Password: 'readarr' 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: find ${TESTSFOLDER} -name "Readarr.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=readarr \ -e POSTGRES_USER=readarr \ -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: 'Readarr.*.osx-core-x64.tar.gz' WindowsCore: osName: 'Windows' testName: 'win-x64' imageName: ${{ variables.windowsImage }} pattern: 'Readarr.*.windows-core-x64.zip' LinuxCore: osName: 'Linux' testName: 'linux-x64' imageName: ${{ variables.linuxImage }} pattern: 'Readarr.*.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/Readarr/. ./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: 'Readarr.*.linux-core-x64.tar.gz' Readarr__Postgres__Host: 'localhost' Readarr__Postgres__Port: '5432' Readarr__Postgres__User: 'readarr' Readarr__Postgres__Password: 'readarr' 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/Readarr/. ./bin/ displayName: Move Package Contents - bash: | docker run -d --name=postgres14 \ -e POSTGRES_PASSWORD=readarr \ -e POSTGRES_USER=readarr \ -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: 'Readarr.*.linux-core-x64.tar.gz' Readarr__Postgres__Host: 'localhost' Readarr__Postgres__Port: '5432' Readarr__Postgres__User: 'readarr' Readarr__Postgres__Password: 'readarr' 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/Readarr/. ./bin/ displayName: Move Package Contents - bash: | docker run -d --name=postgres15 \ -e POSTGRES_PASSWORD=readarr \ -e POSTGRES_USER=readarr \ -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: 'Readarr.*.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/Readarr/. ./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: 'Readarr.*.linux-musl-core-x64.tar.gz' linux-x86: testName: 'linux-x86' artifactName: linux-x86-tests containerImage: ghcr.io/servarr/testimages:linux-x86 pattern: 'Readarr.*.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/Readarr/. ./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: 'Readarr.*.linux-core-x64.tar.gz' failBuild: true Mac: osName: 'Mac' artifactName: 'osx-x64' imageName: ${{ variables.macImage }} pattern: 'Readarr.*.osx-core-x64.tar.gz' failBuild: true Windows: osName: 'Windows' artifactName: 'win-x64' imageName: ${{ variables.windowsImage }} pattern: 'Readarr.*.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/Readarr/. ./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: NodeTool@0 displayName: Set Node.js version inputs: versionSpec: $(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 Readarr 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@1 env: SONAR_SCANNER_OPTS: '' inputs: SonarCloud: 'SonarCloud' organization: 'readarr' scannerMode: 'CLI' configMode: 'manual' cliProjectKey: 'readarrui' cliProjectName: 'ReadarrUI' cliProjectVersion: '$(readarrVersion)' cliSources: './frontend' - task: SonarCloudAnalyze@1 - 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/readarr/readarr/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@1 condition: eq(variables['System.PullRequest.IsFork'], 'False') inputs: SonarCloud: 'SonarCloud' organization: 'readarr' scannerMode: 'MSBuild' projectKey: 'Readarr_Readarr' projectName: 'Readarr' projectVersion: '$(readarrVersion)' extraProperties: | sonar.exclusions=**/obj/**,**/*.dll,**/NzbDrone.Core.Test/Files/**/*,./frontend/**,**/ExternalModules/**,./src/Libraries/** sonar.coverage.exclusions=**/Readarr.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@1 condition: eq(variables['System.PullRequest.IsFork'], 'False') displayName: Publish SonarCloud Results - task: reportgenerator@4 displayName: Generate Coverage Report inputs: reports: '$(Build.SourcesDirectory)/CoverageResults/**/coverage.opencover.xml' targetdir: '$(Build.SourcesDirectory)/CoverageResults/combined' reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges' - task: PublishCodeCoverageResults@1 displayName: Publish Coverage Report inputs: codeCoverageTool: 'cobertura' summaryFileLocation: './CoverageResults/combined/Cobertura.xml' reportDirectory: './CoverageResults/combined/' - 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)