name: $(TeamProject)_$(Build.DefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) parameters: - name: sqlServerIntegrationTests displayName: Run SQL Server Integration Tests type: boolean default: false - name: sqlServerLinuxAcceptanceTests displayName: Run SQL Server Linux Acceptance Tests type: boolean default: false # Skipped due to DB locks, the tests are still being run on the Nightly build - name: sqliteAcceptanceTests displayName: Run SQLite Acceptance Tests type: boolean default: false - name: myGetDeploy displayName: Deploy to MyGet type: boolean default: false - name: nuGetDeploy displayName: Deploy to NuGet type: boolean default: false - name: npmDeploy displayName: Deploy to Npm type: boolean default: false - name: buildApiDocs displayName: Build API docs type: boolean default: false - name: uploadApiDocs displayName: Upload API docs type: boolean default: false - name: forceReleaseTestFilter displayName: Force to use the release test filters type: boolean default: false - name: integrationNonReleaseTestFilter displayName: TestFilter used for non-release type builds type: string default: "--filter TestCategory!=LongRunning&TestCategory!=NonCritical" - name: integrationReleaseTestFilter displayName: TestFilter used for release type builds type: string default: " " - name: nonWindowsIntegrationNonReleaseTestFilter displayName: TestFilter used for non-release type builds on non Windows agents type: string default: "--filter TestCategory!=LongRunning&TestCategory!=NonCritical" - name: nonWindowsIntegrationReleaseTestFilter displayName: TestFilter used for release type builds on non Windows agents type: string default: " " - name: isNightly displayName: "Is nightly build (used for MyGet feed)" type: boolean default: false variables: nodeVersion: 20 solution: umbraco.sln buildConfiguration: Release UMBRACO__CMS__GLOBAL__ID: 00000000-0000-0000-0000-000000000042 DOTNET_NOLOGO: true DOTNET_GENERATE_ASPNET_CERTIFICATE: false DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: true npm_config_cache: $(Pipeline.Workspace)/.npm_client NODE_OPTIONS: --max_old_space_size=16384 stages: ############################################### ## Build ############################################### - stage: Build jobs: - job: A displayName: Build Umbraco CMS pool: vmImage: "windows-latest" steps: - checkout: self submodules: false lfs: false, fetchDepth: 500 - template: templates/backoffice-install.yml - task: UseDotNet@2 displayName: Use .NET SDK from global.json inputs: useGlobalJson: true - task: DotNetCoreCLI@2 displayName: Run dotnet restore inputs: command: restore projects: $(solution) - task: DotNetCoreCLI@2 name: build displayName: Run dotnet build and generate NuGet packages inputs: command: build projects: $(solution) arguments: "--configuration $(buildConfiguration) --no-restore --property:ContinuousIntegrationBuild=true --property:GeneratePackageOnBuild=true --property:PackageOutputPath=$(Build.ArtifactStagingDirectory)/nupkg" - task: PublishPipelineArtifact@1 displayName: Publish nupkg inputs: targetPath: $(Build.ArtifactStagingDirectory)/nupkg artifactName: nupkg - task: PublishPipelineArtifact@1 displayName: Publish build artifacts inputs: targetPath: $(Build.SourcesDirectory) artifactName: build_output - job: B displayName: Build Bellissima Package pool: vmImage: "ubuntu-latest" steps: - checkout: self submodules: false lfs: false, fetchDepth: 500 - template: templates/backoffice-install.yml - script: npm run build:for:npm displayName: Run build:for:npm workingDirectory: src/Umbraco.Web.UI.Client - bash: | echo "##[command]Running npm pack" echo "##[debug]Output directory: $(Build.ArtifactStagingDirectory)" mkdir $(Build.ArtifactStagingDirectory)/npm npm pack --pack-destination $(Build.ArtifactStagingDirectory)/npm mv .npmrc $(Build.ArtifactStagingDirectory)/npm/ displayName: Run npm pack workingDirectory: src/Umbraco.Web.UI.Client - task: PublishPipelineArtifact@1 displayName: Publish Bellissima npm artifact inputs: targetPath: $(Build.ArtifactStagingDirectory)/npm artifactName: npm - stage: Build_Docs condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.buildApiDocs}})) displayName: Prepare API Documentation dependsOn: Build variables: umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['build.NBGV_VersionMajor'] ] jobs: # C# API Reference - job: displayName: Build C# API Reference pool: vmImage: "windows-latest" steps: - task: UseDotNet@2 displayName: Use .NET SDK from global.json inputs: useGlobalJson: true - task: PowerShell@2 displayName: Install DocFX inputs: targetType: inline script: | choco install docfx --version=2.59.4 -y if ($lastexitcode -ne 0){ throw ("Error installing DocFX") } - task: PowerShell@2 displayName: Generate metadata inputs: targetType: inline script: | docfx metadata "$(Build.SourcesDirectory)/build/csharp-docs/docfx.json" if ($lastexitcode -ne 0){ throw ("Error generating metadata.") } - task: PowerShell@2 displayName: Generate documentation inputs: targetType: inline script: | docfx build "$(Build.SourcesDirectory)/build/csharp-docs/docfx.json" if ($lastexitcode -ne 0){ throw ("Error generating documentation.") } - task: ArchiveFiles@2 displayName: Archive C# Docs inputs: rootFolderOrFile: $(Build.SourcesDirectory)/build/csharp-docs/_site includeRootFolder: false archiveFile: $(Build.ArtifactStagingDirectory)/csharp-docs.zip - task: PublishPipelineArtifact@1 displayName: Publish C# Docs inputs: targetPath: $(Build.ArtifactStagingDirectory)/csharp-docs.zip artifact: csharp-docs # js API Reference - job: displayName: Build js API Reference pool: vmImage: "ubuntu-latest" variables: BASE_PATH: /v$(umbracoMajorVersion)/ui steps: - checkout: self submodules: false lfs: false, fetchDepth: 1 fetchFilter: tree:0 - template: templates/backoffice-install.yml - script: npm run storybook:build displayName: Build Storybook env: VITE_BASE_PATH: $(BASE_PATH)/ workingDirectory: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Client - script: sed -i "s|/umbraco/backoffice|$(BASE_PATH)/umbraco/backoffice|" assets/*.js displayName: Replace BASE_PATH on assets workingDirectory: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Client/storybook-static - task: ArchiveFiles@2 displayName: Archive Storybook inputs: rootFolderOrFile: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Client/storybook-static includeRootFolder: false archiveFile: $(Build.ArtifactStagingDirectory)/ui-docs.zip - task: PublishPipelineArtifact@1 displayName: Publish Storybook inputs: targetPath: $(Build.ArtifactStagingDirectory)/ui-docs.zip artifact: ui-docs - script: npm run generate:ui-api-docs displayName: Generate API Docs workingDirectory: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Client - task: ArchiveFiles@2 displayName: Archive UI API Docs inputs: rootFolderOrFile: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Client/ui-api includeRootFolder: false archiveFile: $(Build.ArtifactStagingDirectory)/ui-api-docs.zip - task: PublishPipelineArtifact@1 displayName: Publish UI API Docs inputs: targetPath: $(Build.ArtifactStagingDirectory)/ui-api-docs.zip artifact: ui-api-docs ############################################### ## Test ############################################### - stage: Unit displayName: Unit Tests dependsOn: Build jobs: # Unit Tests - job: displayName: Unit Tests strategy: matrix: Windows: vmImage: "windows-latest" Linux: vmImage: "ubuntu-latest" macOS: vmImage: "macOS-latest" pool: vmImage: $(vmImage) steps: - checkout: self submodules: false lfs: false, fetchDepth: 1 fetchFilter: tree:0 - task: DownloadPipelineArtifact@2 displayName: Download build artifacts inputs: artifact: build_output path: $(Build.SourcesDirectory) - task: UseDotNet@2 displayName: Use .NET SDK from global.json inputs: useGlobalJson: true - task: DotNetCoreCLI@2 displayName: Run dotnet test inputs: command: test projects: "tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj" arguments: "--configuration $(buildConfiguration) --no-build" testRunTitle: Unit Tests - $(Agent.OS) - stage: Integration displayName: Integration Tests dependsOn: Build variables: releaseTestFilter: eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True') jobs: # Integration Tests (SQLite) - job: timeoutInMinutes: 180 displayName: Integration Tests (SQLite) strategy: matrix: # Windows: # vmImage: 'windows-latest' # We split the tests into 3 parts for each OS to reduce the time it takes to run them on the pipeline LinuxPart1Of3: vmImage: "ubuntu-latest" # Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)" LinuxPart2Of3: vmImage: "ubuntu-latest" # Filter tests that are part of the Umbraco.Infrastructure.Service namespace testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)" LinuxPart3Of3: vmImage: "ubuntu-latest" # Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)" macOSPart1Of3: vmImage: "macOS-latest" # Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)" macOSPart2Of3: vmImage: "macOS-latest" # Filter tests that are part of the Umbraco.Infrastructure.Service namespace testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)" macOSPart3Of3: vmImage: "macOS-latest" # Filter tests that are not part of the Umbraco.Infrastructure namespace. testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)" pool: vmImage: $(vmImage) variables: Tests__Database__DatabaseType: "Sqlite" steps: - checkout: self submodules: false lfs: false, fetchDepth: 1 fetchFilter: tree:0 # Setup test environment - task: DownloadPipelineArtifact@2 displayName: Download build artifacts inputs: artifact: build_output path: $(Build.SourcesDirectory) - task: UseDotNet@2 displayName: Use .NET SDK from global.json inputs: useGlobalJson: true # Test - task: DotNetCoreCLI@2 displayName: Run dotnet test inputs: command: test projects: "tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj" testRunTitle: Integration Tests SQLite - $(Agent.OS) ${{ if and(eq(variables['Agent.OS'],'Windows_NT'), or(variables.releaseTestFilter, parameters.forceReleaseTestFilter)) }}: arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build ${{parameters.integrationReleaseTestFilter}}' ${{ elseif eq(variables['Agent.OS'],'Windows_NT') }}: arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build ${{parameters.integrationNonReleaseTestFilter}}' ${{ elseif or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}: arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationReleaseTestFilter}}' ${{ else }}: arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationNonReleaseTestFilter}}' # Integration Tests (SQL Server) - job: timeoutInMinutes: 180 # condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}}) # Outcommented due to timeouts condition: eq(${{parameters.sqlServerIntegrationTests}}, True) displayName: Integration Tests (SQL Server) strategy: matrix: # We split the tests into 3 parts for each OS to reduce the time it takes to run them on the pipeline WindowsPart1Of3: vmImage: "windows-latest" Tests__Database__DatabaseType: LocalDb Tests__Database__SQLServerMasterConnectionString: N/A # Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)" WindowsPart2Of3: vmImage: "windows-latest" Tests__Database__DatabaseType: LocalDb Tests__Database__SQLServerMasterConnectionString: N/A # Filter tests that are part of the Umbraco.Infrastructure.Service namespace testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)" WindowsPart3Of3: vmImage: "windows-latest" Tests__Database__DatabaseType: LocalDb Tests__Database__SQLServerMasterConnectionString: N/A # Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)" LinuxPart1Of3: vmImage: "ubuntu-latest" SA_PASSWORD: UmbracoIntegration123! Tests__Database__DatabaseType: SqlServer Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" # Filter tests that are part of the Umbraco.Infrastructure namespace but not part of the Umbraco.Infrastructure.Service namespace testFilter: "(FullyQualifiedName~Umbraco.Infrastructure) & (FullyQualifiedName!~Umbraco.Infrastructure.Service)" LinuxPart2Of3: vmImage: "ubuntu-latest" SA_PASSWORD: UmbracoIntegration123! Tests__Database__DatabaseType: SqlServer Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" # Filter tests that are part of the Umbraco.Infrastructure.Service namespace testFilter: "(FullyQualifiedName~Umbraco.Infrastructure.Service)" LinuxPart3Of3: vmImage: "ubuntu-latest" SA_PASSWORD: UmbracoIntegration123! Tests__Database__DatabaseType: SqlServer Tests__Database__SQLServerMasterConnectionString: "Server=(local);User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" # Filter tests that are not part of the Umbraco.Infrastructure namespace. So this will run all tests that are not part of the Umbraco.Infrastructure namespace testFilter: "(FullyQualifiedName!~Umbraco.Infrastructure)" pool: vmImage: $(vmImage) steps: # Setup test environment - task: DownloadPipelineArtifact@2 displayName: Download build artifacts inputs: artifact: build_output path: $(Build.SourcesDirectory) - task: UseDotNet@2 displayName: Use .NET SDK from global.json inputs: useGlobalJson: true # Start SQL Server - powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest displayName: Start SQL Server Docker image (Linux) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - powershell: | $maxAttempts = 12 $attempt = 0 $status = "" while (($status -ne 'running') -and ($attempt -lt $maxAttempts)) { Start-Sleep -Seconds 5 # We use the docker inspect command to check the status of the container. If the container is not running, we wait 5 seconds and try again. And if reaches 12 attempts, we fail the build. $status = docker inspect -f '{{.State.Status}}' mssql if ($status -ne 'running') { Write-Host "Waiting for SQL Server to be ready... Attempt $($attempt + 1)" $attempt++ } } if ($status -eq 'running') { Write-Host "SQL Server container is running" docker ps -a } else { Write-Host "SQL Server did not become ready in time. Last known status: $status" docker logs mssql exit 1 } displayName: Wait for SQL Server to be ready (Linux) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - pwsh: SqlLocalDB start MSSQLLocalDB displayName: Start SQL Server LocalDB (Windows) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) # Test - task: DotNetCoreCLI@2 displayName: Run dotnet test inputs: command: test projects: "tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj" testRunTitle: Integration Tests SQL Server - $(Agent.OS) ${{ if and(eq(variables['Agent.OS'],'Windows_NT'), or(variables.releaseTestFilter, parameters.forceReleaseTestFilter)) }}: arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build ${{parameters.integrationReleaseTestFilter}}' ${{ elseif eq(variables['Agent.OS'],'Windows_NT') }}: arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build ${{parameters.integrationNonReleaseTestFilter}}' ${{ elseif or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}: arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationReleaseTestFilter}}' ${{ else }}: arguments: '--filter "$(testFilter)" --configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationNonReleaseTestFilter}}' # Stop SQL Server - pwsh: docker stop mssql displayName: Stop SQL Server Docker image (Linux) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - pwsh: SqlLocalDB stop MSSQLLocalDB displayName: Stop SQL Server LocalDB (Windows) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - stage: E2E displayName: E2E Tests dependsOn: Build variables: npm_config_cache: $(Pipeline.Workspace)/.npm_e2e # Enable console logging in Release mode SERILOG__WRITETO__0__NAME: Async SERILOG__WRITETO__0__ARGS__CONFIGURE__0__NAME: Console # Set unattended install settings UMBRACO__CMS__UNATTENDED__INSTALLUNATTENDED: true UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERNAME: Playwright Test UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD: UmbracoAcceptance123! UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL: playwright@umbraco.com # Custom Umbraco settings UMBRACO__CMS__CONTENT__CONTENTVERSIONCLEANUPPOLICY__ENABLECLEANUP: false UMBRACO__CMS__GLOBAL__DISABLEELECTIONFORSINGLESERVER: true UMBRACO__CMS__GLOBAL__INSTALLMISSINGDATABASE: true UMBRACO__CMS__GLOBAL__ID: 00000000-0000-0000-0000-000000000042 UMBRACO__CMS__GLOBAL__VERSIONCHECKPERIOD: 0 UMBRACO__CMS__GLOBAL__USEHTTPS: true UMBRACO__CMS__HEALTHCHECKS__NOTIFICATION__ENABLED: false UMBRACO__CMS__KEEPALIVE__DISABLEKEEPALIVETASK: true UMBRACO__CMS__WEBROUTING__UMBRACOAPPLICATIONURL: https://localhost:44331/ ASPNETCORE_URLS: https://localhost:44331 jobs: # E2E Tests - job: displayName: E2E Tests (SQLite) # currently disabled due to DB locks randomly occuring. condition: eq(${{parameters.sqliteAcceptanceTests}}, True) variables: # Connection string CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=Umbraco;Mode=Memory;Cache=Shared;Foreign Keys=True;Pooling=True CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.Sqlite strategy: matrix: LinuxPart1Of3: vmImage: "ubuntu-latest" testCommand: "npm run smokeTestSqlite -- --shard=1/3" LinuxPart2Of3: vmImage: "ubuntu-latest" testCommand: "npm run smokeTestSqlite -- --shard=2/3" LinuxPart3Of3: vmImage: "ubuntu-latest" testCommand: "npm run smokeTestSqlite -- --shard=3/3" WindowsPart1Of3: vmImage: "windows-latest" testCommand: "npm run smokeTestSqlite -- --shard=1/3" WindowsPart2Of3: vmImage: "windows-latest" testCommand: "npm run smokeTestSqlite -- --shard=2/3" WindowsPart3Of3: vmImage: "windows-latest" testCommand: "npm run smokeTestSqlite -- --shard=3/3" pool: vmImage: $(vmImage) steps: # Setup test environment - task: DownloadPipelineArtifact@2 displayName: Download NuGet artifacts inputs: artifact: nupkg path: $(Agent.BuildDirectory)/app/nupkg - task: NodeTool@0 displayName: Use Node.js $(nodeVersion) retryCountOnTaskFailure: 3 inputs: versionSpec: $(nodeVersion) - task: UseDotNet@2 displayName: Use .NET SDK from global.json inputs: useGlobalJson: true - pwsh: | "UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL) UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) URL=$(ASPNETCORE_URLS) STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env displayName: Generate .env workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest # Cache and restore NPM packages - task: Cache@2 displayName: Cache NPM packages inputs: key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' restoreKeys: | npm_e2e | "$(Agent.OS)" npm_e2e path: $(npm_config_cache) - script: npm ci --no-fund --no-audit --prefer-offline workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest displayName: Restore NPM packages # Build application - pwsh: | $cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g" dotnet new nugetconfig dotnet nuget add source ./nupkg --name Local dotnet new install Umbraco.Templates::$cmsVersion dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check dotnet restore UmbracoProject cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore dotnet dev-certs https displayName: Build application workingDirectory: $(Agent.BuildDirectory)/app # Run application - bash: | nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!" displayName: Run application (Linux) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) workingDirectory: $(Agent.BuildDirectory)/app - pwsh: | $process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" displayName: Run application (Windows) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) workingDirectory: $(Agent.BuildDirectory)/app # Wait for application to start responding to requests - pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS) displayName: Wait for application workingDirectory: tests/Umbraco.Tests.AcceptanceTest # Install Playwright and dependencies - pwsh: npx playwright install chromium displayName: Install Playwright only with Chromium browser workingDirectory: tests/Umbraco.Tests.AcceptanceTest # Test - pwsh: $(testCommand) displayName: Run Playwright tests workingDirectory: tests/Umbraco.Tests.AcceptanceTest env: CI: true CommitId: $(Build.SourceVersion) AgentOs: $(Agent.OS) # Stop application - bash: kill -15 $(AcceptanceTestProcessId) displayName: Stop application (Linux) condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux')) - pwsh: Stop-Process -Id $(AcceptanceTestProcessId) displayName: Stop application (Windows) condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT')) # Copy artifacts - pwsh: | if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) { Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse } displayName: Copy Playwright results condition: succeededOrFailed() # Copy console error log - pwsh: | if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) { Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory) } displayName: Copy console error log condition: succeededOrFailed() # Publish test artifacts - task: PublishPipelineArtifact@1 displayName: Publish test artifacts condition: succeededOrFailed() inputs: targetPath: $(Build.ArtifactStagingDirectory) artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" # Publish test results - task: PublishTestResults@2 displayName: "Publish test results" condition: succeededOrFailed() inputs: testResultsFormat: 'JUnit' testResultsFiles: '*.xml' searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" testRunTitle: "$(Agent.JobName)" - job: displayName: E2E Tests (SQL Server) variables: # Connection string CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient strategy: matrix: ${{ if eq(parameters.sqlServerLinuxAcceptanceTests, True) }}: LinuxPart1Of3: testCommand: "npm run smokeTest -- --shard=1/3" vmImage: "ubuntu-latest" SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" LinuxPart2Of3: testCommand: "npm run smokeTest -- --shard=2/3" vmImage: "ubuntu-latest" SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" LinuxPart3Of3: testCommand: "npm run smokeTest -- --shard=3/3" vmImage: "ubuntu-latest" SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) CONNECTIONSTRINGS__UMBRACODBDSN: "Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True" WindowsPart1Of3: vmImage: "windows-latest" testCommand: "npm run smokeTest -- --shard=1/3" WindowsPart2Of3: vmImage: "windows-latest" testCommand: "npm run smokeTest -- --shard=2/3" WindowsPart3Of3: vmImage: "windows-latest" testCommand: "npm run smokeTest -- --shard=3/3" pool: vmImage: $(vmImage) steps: # Setup test environment - task: DownloadPipelineArtifact@2 displayName: Download NuGet artifacts inputs: artifact: nupkg path: $(Agent.BuildDirectory)/app/nupkg - task: NodeTool@0 displayName: Use Node.js $(nodeVersion) inputs: versionSpec: $(nodeVersion) - task: UseDotNet@2 displayName: Use .NET SDK from global.json inputs: useGlobalJson: true - pwsh: | "UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL) UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD) URL=$(ASPNETCORE_URLS) STORAGE_STAGE_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/playwright/.auth/user.json CONSOLE_ERRORS_PATH=$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/console-errors.json" | Out-File .env displayName: Generate .env workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest # Cache and restore NPM packages - task: Cache@2 displayName: Cache NPM packages inputs: key: 'npm_e2e | "$(Agent.OS)" | $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/package-lock.json' restoreKeys: | npm_e2e | "$(Agent.OS)" npm_e2e path: $(npm_config_cache) - script: npm ci --no-fund --no-audit --prefer-offline workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest displayName: Restore NPM packages # Build application - pwsh: | $cmsVersion = "$(Build.BuildNumber)" -replace "\+",".g" dotnet new nugetconfig dotnet nuget add source ./nupkg --name Local dotnet new install Umbraco.Templates::$cmsVersion dotnet new umbraco --name UmbracoProject --version $cmsVersion --exclude-gitignore --no-restore --no-update-check dotnet restore UmbracoProject cp $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest.UmbracoProject/*.cs UmbracoProject dotnet build UmbracoProject --configuration $(buildConfiguration) --no-restore dotnet dev-certs https displayName: Build application workingDirectory: $(Agent.BuildDirectory)/app # Start SQL Server - powershell: docker run --name mssql -d -p 1433:1433 -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$(SA_PASSWORD)" mcr.microsoft.com/mssql/server:2022-latest displayName: Start SQL Server Docker image (Linux) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) - pwsh: SqlLocalDB start MSSQLLocalDB displayName: Start SQL Server LocalDB (Windows) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) # Run application - bash: | nohup dotnet run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1 & echo "##vso[task.setvariable variable=AcceptanceTestProcessId]$!" displayName: Run application (Linux) condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) workingDirectory: $(Agent.BuildDirectory)/app - pwsh: | $process = Start-Process dotnet "run --project UmbracoProject --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -NoNewWindow -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log Write-Host "##vso[task.setvariable variable=AcceptanceTestProcessId]$($process.Id)" displayName: Run application (Windows) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) workingDirectory: $(Agent.BuildDirectory)/app # Wait for application to start responding to requests - pwsh: npx wait-on -v --interval 1000 --timeout 120000 $(ASPNETCORE_URLS) displayName: Wait for application workingDirectory: tests/Umbraco.Tests.AcceptanceTest # Install Playwright and dependencies - pwsh: npx playwright install chromium displayName: Install Playwright only with Chromium browser workingDirectory: tests/Umbraco.Tests.AcceptanceTest # Test - pwsh: $(testCommand) displayName: Run Playwright tests workingDirectory: tests/Umbraco.Tests.AcceptanceTest env: CI: true CommitId: $(Build.SourceVersion) AgentOs: $(Agent.OS) # Stop application - bash: kill -15 $(AcceptanceTestProcessId) displayName: Stop application (Linux) condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux')) - pwsh: Stop-Process -Id $(AcceptanceTestProcessId) displayName: Stop application (Windows) condition: and(ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT')) # Stop SQL Server - pwsh: docker stop mssql displayName: Stop SQL Server Docker image (Linux) condition: eq(variables['Agent.OS'], 'Linux') - pwsh: SqlLocalDB stop MSSQLLocalDB displayName: Stop SQL Server LocalDB (Windows) condition: eq(variables['Agent.OS'], 'Windows_NT') # Copy artifacts - pwsh: | if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) { Copy-Item tests/Umbraco.Tests.AcceptanceTest/results/* $(Build.ArtifactStagingDirectory) -Recurse } displayName: Copy Playwright results condition: succeededOrFailed() # Copy console error log - pwsh: | if (Test-Path tests/Umbraco.Tests.AcceptanceTest/console-errors.json) { Copy-Item tests/Umbraco.Tests.AcceptanceTest/console-errors.json $(Build.ArtifactStagingDirectory) } displayName: Copy console error log condition: succeededOrFailed() # Publish test artifacts - task: PublishPipelineArtifact@1 displayName: Publish test artifacts condition: succeededOrFailed() inputs: targetPath: $(Build.ArtifactStagingDirectory) artifact: "Acceptance Test Results - $(Agent.JobName) - Attempt #$(System.JobAttempt)" # Publish test results - task: PublishTestResults@2 displayName: "Publish test results" condition: succeededOrFailed() inputs: testResultsFormat: 'JUnit' testResultsFiles: '*.xml' searchFolder: "tests/Umbraco.Tests.AcceptanceTest/results" testRunTitle: "$(Agent.JobName)" ############################################### ## Release ############################################### - stage: Deploy_MyGet displayName: MyGet pre-release dependsOn: - Unit - Integration - E2E condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.myGetDeploy}})) jobs: - job: pool: vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available. displayName: Push to pre-release feed steps: - checkout: none - task: DownloadPipelineArtifact@2 displayName: Download nupkg inputs: artifact: nupkg path: $(Build.ArtifactStagingDirectory)/nupkg - task: NuGetCommand@2 displayName: NuGet push inputs: command: "push" packagesToPush: $(Build.ArtifactStagingDirectory)/**/*.nupkg nuGetFeedType: "external" ${{ if eq(parameters.isNightly, true) }}: publishFeedCredentials: "MyGet - Umbraco Nightly" ${{ else }}: publishFeedCredentials: "MyGet - Pre-releases" - job: displayName: Push to pre-release feed (npm) steps: - checkout: none - download: current artifact: npm - bash: | # Check if we are on a nightly build if [ $isNightly = "False" ]; then echo "##[debug]Prerelease build detected" registry="https://www.myget.org/F/umbracoprereleases/npm/" else echo "##[debug]Nightly build detected" registry="https://www.myget.org/F/umbraconightly/npm/" fi echo "@umbraco-cms:registry=$registry" >> .npmrc env: isNightly: ${{parameters.isNightly}} workingDirectory: $(Pipeline.Workspace)/npm displayName: Add scoped registry to .npmrc - task: npmAuthenticate@0 displayName: Authenticate with npm (MyGet) inputs: workingFile: "$(Pipeline.Workspace)/npm/.npmrc" customEndpoint: "MyGet (npm) - Umbracoprereleases, MyGet (npm) - Umbraconightly" - bash: | # Setup temp npm project to load in defaults from the local .npmrc npm init -y # Find the first .tgz file in the current directory and publish it files=( ./*.tgz ) npm publish "${files[0]}" displayName: Push to npm (MyGet) workingDirectory: $(Pipeline.Workspace)/npm - stage: Deploy_NuGet displayName: NuGet release dependsOn: - Deploy_MyGet - Build_Docs condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.nuGetDeploy}})) jobs: - job: pool: vmImage: "windows-latest" # NuGetCommand@2 is no longer supported on Ubuntu 24.04 so we'll use windows until an alternative is available. displayName: Push to NuGet steps: - checkout: none - task: DownloadPipelineArtifact@2 displayName: Download nupkg inputs: artifact: nupkg path: $(Build.ArtifactStagingDirectory)/nupkg - task: NuGetCommand@2 displayName: NuGet push inputs: command: "push" packagesToPush: $(Build.ArtifactStagingDirectory)/**/*.nupkg nuGetFeedType: "external" publishFeedCredentials: "NuGet - Umbraco.*" - stage: Deploy_Npm displayName: Npm release condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.npmDeploy}})) dependsOn: - Deploy_NuGet jobs: - job: Publish displayName: Push to NPM steps: - checkout: none - download: current artifact: npm - bash: echo "@umbraco-cms:registry=https://registry.npmjs.org" >> .npmrc workingDirectory: $(Pipeline.Workspace)/npm displayName: Add scoped registry to .npmrc - task: npmAuthenticate@0 displayName: Authenticate with npm inputs: workingFile: $(Pipeline.Workspace)/npm/.npmrc customEndpoint: "NPM - Umbraco Backoffice" - script: | # Setup temp npm project to load in defaults from the local .npmrc npm init -y # Find the first .tgz file in the current directory and publish it files=( ./*.tgz ) npm publish "${files[0]}" displayName: Push to npm workingDirectory: $(Pipeline.Workspace)/npm - stage: Upload_API_Docs pool: vmImage: "windows-latest" # Apparently AzureFileCopy is windows only :( variables: umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['build.NBGV_VersionMajor'] ] displayName: Upload API Documentation dependsOn: - Build - Deploy_NuGet condition: and(succeeded(), or(eq(dependencies.Build.outputs['A.build.NBGV_PublicRelease'], 'True'), ${{parameters.uploadApiDocs}})) jobs: - job: displayName: Upload C# Docs steps: - checkout: none - task: DownloadPipelineArtifact@2 displayName: Download artifact inputs: artifact: csharp-docs path: $(Build.SourcesDirectory) - task: ExtractFiles@1 inputs: archiveFilePatterns: $(Build.SourcesDirectory)/csharp-docs.zip destinationFolder: $(Build.ArtifactStagingDirectory)/csharp-docs overwriteExistingFiles: true - task: AzureFileCopy@4 displayName: "Copy C# Docs to blob storage" inputs: SourcePath: "$(Build.ArtifactStagingDirectory)/csharp-docs/*" azureSubscription: umbraco-storage Destination: AzureBlob storage: umbracoapidocs ContainerName: "$web" BlobPrefix: v$(umbracoMajorVersion)/csharp CleanTargetBeforeCopy: true - job: displayName: Upload Storybook steps: - checkout: none - task: DownloadPipelineArtifact@2 displayName: Download artifact inputs: artifact: ui-docs path: $(Build.SourcesDirectory) - task: ExtractFiles@1 inputs: archiveFilePatterns: $(Build.SourcesDirectory)/ui-docs.zip destinationFolder: $(Build.ArtifactStagingDirectory)/ui-docs overwriteExistingFiles: true - task: AzureFileCopy@4 displayName: "Copy Storybook to blob storage" inputs: SourcePath: "$(Build.ArtifactStagingDirectory)/ui-docs/*" azureSubscription: umbraco-storage Destination: AzureBlob storage: umbracoapidocs ContainerName: "$web" BlobPrefix: v$(umbracoMajorVersion)/ui CleanTargetBeforeCopy: true - job: displayName: Upload UI API Docs steps: - checkout: none - task: DownloadPipelineArtifact@2 displayName: Download artifact inputs: artifact: ui-api-docs path: $(Build.SourcesDirectory) - task: ExtractFiles@1 inputs: archiveFilePatterns: $(Build.SourcesDirectory)/ui-api-docs.zip destinationFolder: $(Build.ArtifactStagingDirectory)/ui-api-docs overwriteExistingFiles: true - task: AzureFileCopy@4 displayName: "Copy UI API Docs to blob storage" inputs: SourcePath: "$(Build.ArtifactStagingDirectory)/ui-api-docs/*" azureSubscription: umbraco-storage Destination: AzureBlob storage: umbracoapidocs ContainerName: "$web" BlobPrefix: v$(umbracoMajorVersion)/ui-api CleanTargetBeforeCopy: true