Merge remote-tracking branch 'origin/v13/dev' into v14/dev

# Conflicts:
#	.artifactignore
#	build/azure-pipelines.yml
#	tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker
This commit is contained in:
Jacob Overgaard
2024-02-02 13:48:28 +01:00
13 changed files with 442 additions and 247 deletions

View File

@@ -1,4 +1,4 @@
**/*
!**/bin/**
!**/obj/**
!tests/Umbraco.Tests.Integration/bin/**
!tests/Umbraco.Tests.UnitTests/bin/**
**/node_modules

View File

@@ -5,6 +5,10 @@ parameters:
displayName: Run SQL Server Integration Tests
type: boolean
default: false
- name: sqlServerAcceptanceTests
displayName: Run SQL Server Acceptance Tests
type: boolean
default: false
- name: myGetDeploy
displayName: Deploy to MyGet
type: boolean
@@ -38,11 +42,11 @@ parameters:
type: string
default: ' '
- name: nonWindowsIntegrationNonReleaseTestFilter
displayName: TestFilter used for non-release type builds on non windows agents
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
displayName: TestFilter used for release type builds on non Windows agents
type: string
default: ' '
- name: isNightly
@@ -52,8 +56,6 @@ parameters:
variables:
nodeVersion: 20
dotnetVersion: 8.x
dotnetIncludePreviewVersions: true
solution: umbraco.sln
buildConfiguration: Release
UMBRACO__CMS__GLOBAL__ID: 00000000-0000-0000-0000-000000000042
@@ -116,11 +118,9 @@ stages:
displayName: Run Login Build (Bellissima)
workingDirectory: src/Umbraco.Web.UI.New.Client/apps/auth
- task: UseDotNet@2
displayName: Use .NET $(dotnetVersion)
displayName: Use .NET SDK from global.json
inputs:
version: $(dotnetVersion)
performMultiLevelLookup: true
includePreviewVersions: $(dotnetIncludePreviewVersions)
useGlobalJson: true
- task: DotNetCoreCLI@2
displayName: Run dotnet restore
inputs:
@@ -294,16 +294,14 @@ stages:
artifact: build_output
path: $(Build.SourcesDirectory)
- task: UseDotNet@2
displayName: Use .NET $(dotnetVersion)
displayName: Use .NET SDK from global.json
inputs:
version: $(dotnetVersion)
performMultiLevelLookup: true
includePreviewVersions: $(dotnetIncludePreviewVersions)
useGlobalJson: true
- task: DotNetCoreCLI@2
displayName: Run dotnet test
inputs:
command: test
projects: '**/*.Tests.UnitTests.csproj'
projects: 'tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj'
arguments: '--configuration $(buildConfiguration) --no-build'
testRunTitle: Unit Tests - $(Agent.OS)
@@ -326,48 +324,36 @@ stages:
vmImage: 'macOS-latest'
pool:
vmImage: $(vmImage)
variables:
Tests__Database__DatabaseType: 'Sqlite'
steps:
- checkout: self
submodules: true
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download build artifacts
inputs:
artifact: build_output
path: $(Build.SourcesDirectory)
- task: UseDotNet@2
displayName: Use .NET $(dotnetVersion)
displayName: Use .NET SDK from global.json
inputs:
version: $(dotnetVersion)
performMultiLevelLookup: true
includePreviewVersions: $(dotnetIncludePreviewVersions)
useGlobalJson: true
# Test
- task: DotNetCoreCLI@2
displayName: Run dotnet test Windows
condition: eq(variables['Agent.OS'],'Windows_NT')
displayName: Run dotnet test
inputs:
command: test
projects: '**/*.Tests.Integration.csproj'
projects: 'tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj'
testRunTitle: Integration Tests SQLite - $(Agent.OS)
${{ if or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
${{ if and(eq(variables['Agent.OS'],'Windows_NT'), or(variables.releaseTestFilter, parameters.forceReleaseTestFilter)) }}:
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.integrationReleaseTestFilter}}'
${{ else }}:
arguments: '--configuration $(buildConfiguration) ${{parameters.integrationNonReleaseTestFilter}}'
env:
Tests__Database__DatabaseType: 'Sqlite'
Umbraco__CMS__Global__MainDomLock: 'FileSystemMainDomLock'
- task: DotNetCoreCLI@2
displayName: Run dotnet test Non Windows
condition: ne(variables['Agent.OS'],'Windows_NT')
inputs:
command: test
projects: '**/*.Tests.Integration.csproj'
testRunTitle: Integration Tests SQLite - $(Agent.OS)
${{ if or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
${{ elseif eq(variables['Agent.OS'],'Windows_NT') }}:
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.integrationNonReleaseTestFilter}}'
${{ elseif or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationReleaseTestFilter}}'
${{ else }}:
arguments: '--configuration $(buildConfiguration) ${{parameters.nonWindowsIntegrationNonReleaseTestFilter}}'
env:
Tests__Database__DatabaseType: 'Sqlite'
Umbraco__CMS__Global__MainDomLock: 'FileSystemMainDomLock'
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationNonReleaseTestFilter}}'
# Integration Tests (SQL Server)
- job:
@@ -378,208 +364,363 @@ stages:
matrix:
Windows:
vmImage: 'windows-latest'
testDb: LocalDb
connectionString: N/A
Tests__Database__DatabaseType: LocalDb
Tests__Database__SQLServerMasterConnectionString: N/A
Linux:
vmImage: 'ubuntu-latest'
testDb: SqlServer
connectionString: 'Server=localhost,1433;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=true'
SA_PASSWORD: UmbracoIntegration123!
Tests__Database__DatabaseType: SqlServer
Tests__Database__SQLServerMasterConnectionString: 'Server=(local);User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True'
pool:
vmImage: $(vmImage)
variables:
SA_PASSWORD: UmbracoIntegration123!
steps:
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download build artifacts
inputs:
artifact: build_output
path: $(Build.SourcesDirectory)
- task: UseDotNet@2
displayName: Use .NET $(dotnetVersion)
displayName: Use .NET SDK from global.json
inputs:
version: $(dotnetVersion)
includePreviewVersions: $(dotnetIncludePreviewVersions)
- powershell: sqllocaldb start mssqllocaldb
displayName: Start localdb (Windows only)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
- powershell: docker run --name mssql -d -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=$(SA_PASSWORD) -e MSSQL_PID=Developer mcr.microsoft.com/mssql/server:2019-latest
displayName: Start SQL Server (Linux only)
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'))
- 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 Windows
condition: eq(variables['Agent.OS'],'Windows_NT')
displayName: Run dotnet test
inputs:
command: test
projects: '**/*.Tests.Integration.csproj'
projects: 'tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj'
testRunTitle: Integration Tests SQL Server - $(Agent.OS)
${{ if or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
${{ if and(eq(variables['Agent.OS'],'Windows_NT'), or(variables.releaseTestFilter, parameters.forceReleaseTestFilter)) }}:
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.integrationReleaseTestFilter}}'
${{ else }}:
${{ elseif eq(variables['Agent.OS'],'Windows_NT') }}:
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.integrationNonReleaseTestFilter}}'
env:
Tests__Database__DatabaseType: $(testDb)
Tests__Database__SQLServerMasterConnectionString: $(connectionString)
Umbraco__CMS__Global__MainDomLock: 'SqlMainDomLock'
- task: DotNetCoreCLI@2
displayName: Run dotnet test NonWindows
condition: ne(variables['Agent.OS'],'Windows_NT')
inputs:
command: test
projects: '**/*.Tests.Integration.csproj'
testRunTitle: Integration Tests SQL Server - $(Agent.OS)
${{ if or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
${{ elseif or(variables.releaseTestFilter, parameters.forceReleaseTestFilter) }}:
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationReleaseTestFilter}}'
${{ else }}:
arguments: '--configuration $(buildConfiguration) --no-build ${{parameters.nonWindowsIntegrationNonReleaseTestFilter}}'
env:
Tests__Database__DatabaseType: $(testDb)
Tests__Database__SQLServerMasterConnectionString: $(connectionString)
Umbraco__CMS__Global__MainDomLock: 'SqlMainDomLock'
# 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
variables:
npm_config_cache: $(Pipeline.Workspace)/.npm_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
timeoutInMinutes: 120
displayName: E2E Tests (SQLite)
variables:
Umbraco__CMS__Unattended__UnattendedUserName: Playwright Test
Umbraco__CMS__Unattended__UnattendedUserPassword: UmbracoAcceptance123!
Umbraco__CMS__Unattended__UnattendedUserEmail: playwright@umbraco.com
ASPNETCORE_URLS: https://localhost:8443
# Connection string
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=Umbraco;Mode=Memory;Cache=Shared;Foreign Keys=True;Pooling=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.Sqlite
strategy:
matrix:
Linux:
vmImage: 'ubuntu-latest'
dockerfile: umbraco-linux.docker
dockerImageName: umbraco-linux
Windows:
vmImage: 'windows-latest'
DOTNET_GENERATE_ASPNET_CERTIFICATE: true # Automatically generate HTTPS development certificate on Windows
# 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__Global__InstallMissingDatabase: true
UmbracoDatabaseServer: (LocalDB)\MSSQLLocalDB
UmbracoDatabaseName: AcceptanceTestDB
ConnectionStrings__umbracoDbDSN: Server=$(UmbracoDatabaseServer);Database=$(UmbracoDatabaseName);Integrated Security=true;
# Custom Umbraco settings
Umbraco__CMS__Global__VersionCheckPeriod: 0
Umbraco__CMS__Global__UseHttps: true
Umbraco__CMS__HealthChecks__Notification__Enabled: false
Umbraco__CMS__KeepAlive__DisableKeepAliveTask: true
pool:
vmImage: $(vmImage)
steps:
# Setup test environment
- task: DownloadPipelineArtifact@2
displayName: Download nupkg
displayName: Download NuGet artifacts
inputs:
artifact: nupkg
path: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/misc/nupkg
path: $(Agent.BuildDirectory)/app/nupkg
- task: NodeTool@0
displayName: Use Node.js $(nodeVersion)
retryCountOnTaskFailure: 3
inputs:
versionSpec: $(nodeVersion)
- task: Cache@2
displayName: Cache node_modules
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)
- pwsh: |
New-Item -Path "." -Name ".env" -ItemType "file" -Value "UMBRACO_USER_LOGIN=$(Umbraco__CMS__Unattended__UnattendedUserEmail)
UMBRACO_USER_PASSWORD=$(Umbraco__CMS__Unattended__UnattendedUserPassword)
URL=$(ASPNETCORE_URLS)"
displayName: Generate .env
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/
- script: npm ci --no-fund --no-audit --prefer-offline
workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/
displayName: Run npm ci
- pwsh: sqllocaldb start mssqllocaldb
displayName: Start localdb (Windows only)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
- pwsh: Invoke-Sqlcmd -Query "CREATE DATABASE $env:UmbracoDatabaseName" -ServerInstance $env:UmbracoDatabaseServer
displayName: Create database (Windows only)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
- task: UseDotNet@2
displayName: Use .NET $(dotnetVersion)
displayName: Use .NET SDK from global.json
inputs:
version: $(dotnetVersion)
performMultiLevelLookup: true
includePreviewVersions: $(dotnetIncludePreviewVersions)
useGlobalJson: true
- pwsh: |
$sha = 'g$(Build.SourceVersion)'.substring(0, 8)
docker build -t $(dockerImageName):$sha -f $(dockerfile) .
mkdir -p $(Build.ArtifactStagingDirectory)/docker-images
docker save -o $(Build.ArtifactStagingDirectory)/docker-images/$(dockerImageName).$sha.tar $(dockerImageName):$sha
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p $(Umbraco__CMS__Unattended__UnattendedUserPassword)
docker run --name $(dockerImageName) -dp 8080:5000 -dp 8443:5001 -e UMBRACO__CMS__GLOBAL__ID=$(UMBRACO__CMS__GLOBAL__ID) -e ASPNETCORE_Kestrel__Certificates__Default__Password="$(Umbraco__CMS__Unattended__UnattendedUserPassword)" -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx -v ${HOME}/.aspnet/https:/https/ $(dockerImageName):$sha
docker ps
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
displayName: Build and run container (Linux only)
workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc
"UMBRACO_USER_LOGIN=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSEREMAIL)
UMBRACO_USER_PASSWORD=$(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
URL=$(ASPNETCORE_URLS)" | 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: |
dotnet new --install ./nupkg/Umbraco.Templates.*.nupkg
dotnet new umbraco --name AcceptanceTestProject --no-restore --output .
dotnet restore --configfile ./nuget.config
dotnet build --configuration $(buildConfiguration) --no-restore
$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
$process = Start-Process -FilePath "dotnet" -ArgumentList "run --configuration $(buildConfiguration) --no-build --no-launch-profile 2>&1" -PassThru -RedirectStandardOutput $(Build.ArtifactStagingDirectory)/playwright.log
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'))
displayName: Build and run app (Windows only)
workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc
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 app
displayName: Wait for application
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Install Playwright and dependencies
- pwsh: npx playwright install --with-deps
displayName: Install Playwright
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: npm run test --ignore-certificate-errors
displayName: Run Playwright (Desktop)
displayName: Run Playwright tests
continueOnError: true
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(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux'))
- pwsh: Stop-Process -Id $(AcceptanceTestProcessId)
displayName: Stop application (Windows)
condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT'))
# Copy artifacts
- pwsh: |
docker logs $(dockerImageName) > $(Build.ArtifactStagingDirectory)/playwright.log 2>&1
docker stop $(dockerImageName)
condition: eq(variables['Agent.OS'], 'Linux')
displayName: Stop app (Linux only)
- pwsh: Stop-Process $env:AcceptanceTestProcessId
condition: eq(variables['Agent.OS'], 'Windows_NT')
displayName: Stop app (Windows only)
- task: PowerShell@2
displayName: Check if artifacts folder exists
inputs:
targetType: inline
script: |
$MyVariable = Test-Path -Path $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/results
Write-Host "##vso[task.setvariable variable=resultFolderExists;]$MyVariable"
- task: CopyFiles@2
displayName: Prepare artifacts
condition: eq(variables.resultFolderExists, 'True')
inputs:
sourceFolder: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/results/
targetFolder: $(Build.ArtifactStagingDirectory)/playwright
if (Test-Path tests/Umbraco.Tests.AcceptanceTest/results/*) {
Copy-Item tests/Umbraco.Tests.AcceptanceTest/results $(Build.ArtifactStagingDirectory) -Recurse
}
displayName: Copy Playwright results
condition: succeededOrFailed()
# Publish
- task: PublishPipelineArtifact@1
condition: always()
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: 'E2E artifacts - $(Agent.OS) - Attempt #$(System.JobAttempt)'
artifact: 'Acceptance Tests - $(Agent.JobName) - Attempt #$(System.JobAttempt)'
- job:
displayName: E2E Tests (SQL Server)
condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerAcceptanceTests}})
variables:
# Connection string
CONNECTIONSTRINGS__UMBRACODBDSN: Data Source=(localdb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Umbraco.mdf;Integrated Security=True
CONNECTIONSTRINGS__UMBRACODBDSN_PROVIDERNAME: Microsoft.Data.SqlClient
strategy:
matrix:
Linux:
vmImage: 'ubuntu-latest'
SA_PASSWORD: $(UMBRACO__CMS__UNATTENDED__UNATTENDEDUSERPASSWORD)
CONNECTIONSTRINGS__UMBRACODBDSN: 'Server=(local);Database=Umbraco;User Id=sa;Password=$(SA_PASSWORD);TrustServerCertificate=True'
Windows:
vmImage: 'windows-latest'
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)" | 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 --with-deps
displayName: Install Playwright
workingDirectory: tests/Umbraco.Tests.AcceptanceTest
# Test
- pwsh: npm run test --ignore-certificate-errors
displayName: Run Playwright tests
continueOnError: true
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(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Linux'))
- pwsh: Stop-Process -Id $(AcceptanceTestProcessId)
displayName: Stop application (Windows)
condition: and(succeeded(), ne(variables.AcceptanceTestProcessId, ''), eq(variables['Agent.OS'], 'Windows_NT'))
# 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'))
# 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()
# Publish
- task: PublishPipelineArtifact@1
displayName: Publish test artifacts
condition: succeededOrFailed()
inputs:
targetPath: $(Build.ArtifactStagingDirectory)
artifact: 'Acceptance Tests - $(Agent.JobName) - Attempt #$(System.JobAttempt)'
###############################################
## Release

View File

@@ -1,7 +1,6 @@
{
"sdk": {
"version": "8.0.0",
"rollForward": "latestFeature",
"allowPrerelease": false
"version": "8.0.100",
"rollForward": "latestFeature"
}
}

View File

@@ -112,7 +112,8 @@ internal abstract class NestedPropertyIndexValueFactoryBase<TSerialized, TItem>
published,
propertyTypeDictionary,
nestedContentRowValue,
availableCultures));
availableCultures,
contentTypeDictionary));
index++;
}
@@ -213,7 +214,8 @@ internal abstract class NestedPropertyIndexValueFactoryBase<TSerialized, TItem>
bool published,
IDictionary<string, IPropertyType> propertyTypeDictionary,
TItem nestedContentRowValue,
IEnumerable<string> availableCultures)
IEnumerable<string> availableCultures,
IDictionary<Guid,IContentType> contentTypeDictionary)
{
foreach ((var propertyAlias, var propertyValue) in GetRawProperty(nestedContentRowValue))
{
@@ -238,7 +240,7 @@ internal abstract class NestedPropertyIndexValueFactoryBase<TSerialized, TItem>
subProperty.PublishValues(availableCulture, segment ?? "*");
}
indexValues =
editor.PropertyIndexValueFactory.GetIndexValues(subProperty, availableCulture, segment, published, availableCultures);
editor.PropertyIndexValueFactory.GetIndexValues(subProperty, availableCulture, segment, published, availableCultures, contentTypeDictionary);
}
}
else
@@ -248,7 +250,7 @@ internal abstract class NestedPropertyIndexValueFactoryBase<TSerialized, TItem>
{
subProperty.PublishValues(culture ?? "*", segment ?? "*");
}
indexValues = editor.PropertyIndexValueFactory.GetIndexValues(subProperty, culture, segment, published, availableCultures);
indexValues = editor.PropertyIndexValueFactory.GetIndexValues(subProperty, culture, segment, published, availableCultures, contentTypeDictionary);
}
foreach ((var nestedAlias, IEnumerable<object?> nestedValue) in indexValues)

View File

@@ -520,7 +520,7 @@ public static class ImageCropperTemplateCoreExtensions
throw new ArgumentNullException(nameof(mediaItem));
}
if (mediaItem.HasProperty(propertyAlias) == false || mediaItem.HasValue(propertyAlias) == false)
if (mediaItem.HasProperty(propertyAlias) == false || mediaItem.HasValue(publishedValueFallback, propertyAlias) == false)
{
return null;
}

View File

@@ -0,0 +1,42 @@
using Microsoft.Data.Sqlite;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Extensions;
namespace UmbracoProject;
/// <summary>
/// Ensures a SQLite in-memory database is persisted for the whole application duration.
/// </summary>
public sealed class SQLiteMemoryComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
var connectionString = builder.Config.GetUmbracoConnectionString(out var providerName);
if (!string.IsNullOrEmpty(connectionString) &&
Constants.ProviderNames.SQLLite.InvariantEquals(providerName) &&
connectionString.InvariantContains("Mode=Memory"))
{
// Open new SQLite connection to ensure in-memory database is persisted for the whole application duration
var connection = new SqliteConnection(connectionString);
connection.Open();
// And ensure connection is kept open (by keeping a reference) and gets gracefully closed/disposed when application stops
builder.Services.AddHostedService(_ => new SQLiteMemoryHostedService(connection));
}
}
private sealed class SQLiteMemoryHostedService : IHostedService, IAsyncDisposable
{
private readonly SqliteConnection _connection;
public SQLiteMemoryHostedService(SqliteConnection connection) => _connection = connection;
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public async Task StopAsync(CancellationToken cancellationToken) => await _connection.CloseAsync();
public async ValueTask DisposeAsync() => await _connection.DisposeAsync();
}
}

View File

@@ -0,0 +1,44 @@
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Extensions;
namespace UmbracoProject;
/// <summary>
/// Disable waiting on log IO to finish when commiting a transaction (we can tolerate some data loss) on SQL Server.
/// </summary>
public sealed class SqlServerDelayedDurabilityComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
var connectionString = builder.Config.GetUmbracoConnectionString(out var providerName);
if (!string.IsNullOrEmpty(connectionString) &&
Constants.ProviderNames.SQLServer.InvariantEquals(providerName))
{
builder.AddNotificationAsyncHandler<UnattendedInstallNotification, SqlServerDelayedDurabilityInstallNotification>();
}
}
private sealed class SqlServerDelayedDurabilityInstallNotification : INotificationAsyncHandler<UnattendedInstallNotification>
{
private readonly IOptions<ConnectionStrings> _connectionStrings;
public SqlServerDelayedDurabilityInstallNotification(IOptions<ConnectionStrings> connectionStrings) => _connectionStrings = connectionStrings;
public async Task HandleAsync(UnattendedInstallNotification notification, CancellationToken cancellationToken)
{
using var connection = new SqlConnection(_connectionStrings.Value.ConnectionString);
await connection.OpenAsync(cancellationToken);
// Disable waiting on log IO to finish when commiting a transaction (we can tolerate some data loss)
var command = new SqlCommand("ALTER DATABASE CURRENT SET DELAYED_DURABILITY = FORCED;", connection);
await command.ExecuteNonQueryAsync(cancellationToken);
}
}
}

View File

@@ -0,0 +1,13 @@
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Infrastructure;
namespace UmbracoProject;
/// <summary>
/// Suspends/disables scheduled publishing, because that takes an eager write lock every minute, resulting in flaky test runs on SQLite.
/// </summary>
public sealed class SuspendScheduledPublishingComposer : IComposer
{
public void Compose(IUmbracoBuilder builder) => Suspendable.ScheduledPublishing.Suspend();
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<!-- All C# files in the root of this project will be copied to the E2E/Acceptance Test application during build -->
<OutputType>Library</OutputType>
<RootNamespace>UmbracoProject</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Umbraco.Cms\Umbraco.Cms.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<!--
Keep this empty Directory.Build.props file to prevent inheriting from parent directories,
because the Windows E2E test that outputs a new Umbraco project into this directory otherwise won't build.
-->
</Project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="local" value="./nupkg" />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View File

@@ -1,51 +0,0 @@
############################################
## Build
############################################
FROM mcr.microsoft.com/dotnet/nightly/sdk:8.0.100-rc.2-jammy AS build
COPY nuget.config .
COPY nuget.config .
WORKDIR /nupkg
COPY nupkg .
WORKDIR /build
RUN dotnet new --install /nupkg/Umbraco.Templates.*.nupkg
RUN dotnet new umbraco --name AcceptanceTestProject --no-restore --output .
RUN dotnet restore --configfile /nuget.config
RUN dotnet build --configuration Release --no-restore
RUN dotnet publish --configuration Release --no-build --output /dist
############################################
## Run
############################################
FROM mcr.microsoft.com/dotnet/nightly/aspnet:8.0.0-rc.2-jammy AS run
WORKDIR /app
COPY --from=build dist .
# Enable console logging in Release mode
ENV Serilog__WriteTo__0__Name=Async
ENV Serilog__WriteTo__0__Args__configure__0__Name=Console
# Set unattended install settings
ENV ConnectionStrings__umbracoDbDSN_ProviderName="Microsoft.Data.Sqlite"
ENV ConnectionStrings__umbracoDbDSN="Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True"
ENV Umbraco__CMS__Unattended__InstallUnattended="true"
ENV Umbraco__CMS__Unattended__UnattendedUserName="Playwright Test"
ENV Umbraco__CMS__Unattended__UnattendedUserEmail="playwright@umbraco.com"
ENV Umbraco__CMS__Unattended__UnattendedUserPassword="UmbracoAcceptance123!"
# Custom Umbraco settings
ENV Umbraco__CMS__Global__VersionCheckPeriod="0"
ENV Umbraco__CMS__Global__UseHttps="true"
ENV Umbraco__CMS__HealthChecks__Notification__Enabled="false"
ENV Umbraco__CMS__KeepAlive__DisableKeepAliveTask="true"
# Set application URL
ENV ASPNETCORE_URLS="http://0.0.0.0:5000;https://0.0.0.0:5001"
CMD dotnet AcceptanceTestProject.dll

View File

@@ -140,14 +140,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
global.json = global.json
icon.png = icon.png
LICENSE.md = LICENSE.md
umbraco.sln.DotSettings = umbraco.sln.DotSettings
nuget.config = nuget.config
umbraco.sln.DotSettings = umbraco.sln.DotSettings
version.json = version.json
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{20CE9C97-9314-4A19-BCF1-D12CF49B7205}"
ProjectSection(SolutionItems) = preProject
build\azure-pipelines.yml = build\azure-pipelines.yml
build\nightly-build-trigger.yml = build\nightly-build-trigger.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "csharp-docs", "csharp-docs", "{F2BF84D9-0A14-40AF-A0F3-B9BBBBC16A44}"
@@ -214,6 +215,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.EFC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.EFCore.SqlServer", "src\Umbraco.Cms.Persistence.EFCore.SqlServer\Umbraco.Cms.Persistence.EFCore.SqlServer.csproj", "{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.AcceptanceTest.UmbracoProject", "tests\Umbraco.Tests.AcceptanceTest.UmbracoProject\Umbraco.Tests.AcceptanceTest.UmbracoProject.csproj", "{A13FF0A0-69FA-468A-9F79-565401D5C341}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -408,6 +411,12 @@ Global
{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}.Release|Any CPU.Build.0 = Release|Any CPU
{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU
{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}.SkipTests|Any CPU.Build.0 = Debug|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.Release|Any CPU.Build.0 = Release|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU
{A13FF0A0-69FA-468A-9F79-565401D5C341}.SkipTests|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -429,6 +438,7 @@ Global
{EA628ABD-624E-4AF3-B548-6710D4D66531} = {2B47AD9F-FFF1-448A-88F1-D4F568811738}
{C55CA725-9F4E-4618-9435-6B8AE05DA14D} = {995D9EFA-8BB1-4333-80AD-C525A06FD984}
{D88A926B-E8D6-495A-A2ED-8EFD0C847C62} = {995D9EFA-8BB1-4333-80AD-C525A06FD984}
{A13FF0A0-69FA-468A-9F79-565401D5C341} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC}