diff --git a/.artifactignore b/.artifactignore new file mode 100644 index 0000000000..a287e295ae --- /dev/null +++ b/.artifactignore @@ -0,0 +1,3 @@ +**/* +!**/bin/** +!**/obj/** diff --git a/.gitignore b/.gitignore index 348474c8cb..26f96f89b7 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,10 @@ preserve.belle /src/Umbraco.Web.UI.Docs/api/ /src/Umbraco.Web.UI.Docs/package-lock.json +# csharp-docs +/build/csharp-docs/api/ +/build/csharp-docs/_site/ + # Build /build.out/ /build.tmp/ @@ -64,8 +68,6 @@ preserve.belle /build/docs.zip /build/ui-docs.zip /build/csharp-docs.zip -/build/ApiDocs/ -/src/ApiDocs/api/ /src/Umbraco.Cms.StaticAssets/wwwroot/umbraco/ # Environment specific data @@ -102,4 +104,5 @@ cypress.env.json /tests/Umbraco.Tests.UnitTests/[Uu]mbraco/[Dd]ata/TEMP/ # Ignore auto-generated schema -/src/Umbraco.Web.UI/[Uu]mbraco/config/appsettings-schema.json +/src/Umbraco.Web.UI/appsettings-schema.json +/src/Umbraco.Cms/appsettings-schema.json diff --git a/Directory.Build.props b/Directory.Build.props index fcf605f555..71be10afd7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,4 +4,15 @@ + + + + all + 3.5.103 + + + + all + + diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec deleted file mode 100644 index f105f33cda..0000000000 --- a/build/NuSpecs/UmbracoCms.nuspec +++ /dev/null @@ -1,31 +0,0 @@ - - - - Umbraco.Cms - 10.0.0 - Umbraco Cms - Umbraco HQ - Umbraco HQ - MIT - https://umbraco.com/ - https://umbraco.com/dist/nuget/logo-small.png - false - Installs Umbraco Cms in your Visual Studio ASP.NET Core project - Installs Umbraco Cms in your Visual Studio ASP.NET Core project - en-US - umbraco - - - - - - - - - - - - - - - diff --git a/build/azure-pipelines.yml b/build/azure-pipelines.yml index 9657898c0d..2a7d43655d 100644 --- a/build/azure-pipelines.yml +++ b/build/azure-pipelines.yml @@ -1,721 +1,557 @@ name: $(TeamProject)_$(Build.DefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) + +parameters: + - name: sqlServerIntegrationTests + displayName: Run SQL Server Integration 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: buildApiDocs + displayName: Build API docs + type: boolean + default: false + - name: uploadApiDocs + displayName: Upload API docs + type: boolean + default: false + variables: - buildConfiguration: Release - SA_PASSWORD: UmbracoIntegration123! - UMBRACO__CMS_GLOBAL__ID: 00000000-0000-0000-0000-000000000042 - UmbracoBuild: AzurePipeline - nodeVersion: 14.18.1 -resources: - containers: - - container: mssql - image: 'mcr.microsoft.com/mssql/server:2017-latest' - env: - ACCEPT_EULA: 'Y' - SA_PASSWORD: $(SA_PASSWORD) - MSSQL_PID: Developer - ports: - - '1433:1433' - options: '--name mssql' + buildConfiguration: Release + SA_PASSWORD: UmbracoIntegration123! + UMBRACO__CMS_GLOBAL__ID: 00000000-0000-0000-0000-000000000042 + nodeVersion: 14.18.1 + DOTNET_NOLOGO: 1 + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + stages: - - stage: Determine_build_type - displayName: Determine build type - dependsOn: [ ] - jobs: - - job: Set_build_variables - displayName: Set build variables - pool: - vmImage: windows-latest - steps: - - task: PowerShell@1 - name: setReleaseVariable - displayName: Set isRelease variable - inputs: - scriptType: inlineScript - inlineScript: > - $isRelease = [regex]::matches($env:BUILD_SOURCEBRANCH,"v\d+\/\d+.\d+.*") + ############################################### + ## Build + ############################################### + - stage: Build + variables: + npm_config_cache: $(Pipeline.Workspace)/.npm_client + jobs: + - job: A + displayName: Build Umbraco CMS + pool: + vmImage: 'ubuntu-latest' + steps: + - task: NodeTool@0 + displayName: Use node $(nodeVersion) + inputs: + versionSpec: $(nodeVersion) + - task: Cache@2 + displayName: Cache node_modules + inputs: + key: '"npm_client" | "$(Agent.OS)" | $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Client/package-lock.json' + restoreKeys: | + npm_client | "$(Agent.OS)" + path: $(npm_config_cache) + - script: npm ci --no-fund --no-audit --prefer-offline + workingDirectory: src/Umbraco.Web.UI.Client + displayName: Run npm ci + - task: gulp@0 + displayName: Run gulp build + inputs: + gulpFile: src/Umbraco.Web.UI.Client/gulpfile.js + targets: coreBuild + workingDirectory: src/Umbraco.Web.UI.Client + - task: DotNetCoreCLI@2 + displayName: Run dotnet build + inputs: + command: build + projects: umbraco.sln + arguments: '--configuration $(buildConfiguration)' + - script: | + version="$(Build.BuildNumber)" + echo "varsion: $version" - if ($isRelease.Count -gt 0){ - Write-Host "##vso[build.addbuildtag]Release build" - Write-Host "##vso[task.setvariable variable=isRelease;isOutput=true]true" - }else{ - Write-Host "##vso[task.setvariable variable=isRelease;isOutput=true]false" - } - - stage: Unit_Tests - displayName: Unit Tests - dependsOn: [] - jobs: - - job: Linux_Unit_Tests - displayName: Linux - pool: - vmImage: ubuntu-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: '**/umbraco.sln' - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: test - projects: '**/*.Tests.UnitTests.csproj' - arguments: '--no-build' - - job: MacOS_Unit_Tests - displayName: Mac OS - pool: - vmImage: macOS-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: '**/umbraco.sln' - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: test - projects: '**/*.Tests.UnitTests.csproj' - arguments: '--no-build' - - job: Windows_Unit_Tests - displayName: Windows - pool: - vmImage: windows-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: '**/umbraco.sln' - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: test - projects: '**/*.Tests.UnitTests.csproj' - arguments: '--no-build' + major="$(echo $version | cut -d '.' -f 1)" + echo "major version: $major" - - stage: Integration_Tests - displayName: Integration Tests - dependsOn: [] - jobs: + echo "##vso[task.setvariable variable=majorVersion;isOutput=true]$major" + displayName: Set major version + name: determineMajorVersion + - task: PowerShell@2 + displayName: Prepare nupkg + inputs: + targetType: inline + script: | + $umbracoVersion = "$(Build.BuildNumber)" -replace "\+",".g" + $templatePaths = Get-ChildItem 'templates/**/.template.config/template.json' - - job: Linux_Integration_Tests_SQLite - timeoutInMinutes: 120 - displayName: Linux (SQLite) - pool: - vmImage: ubuntu-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: '**/umbraco.sln' - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: test - projects: '**/Umbraco.Tests.Integration.csproj' - arguments: '--no-build' - env: - Tests__Database__DatabaseType: 'Sqlite' - Umbraco__Cms__global__MainDomLock: 'FileSystemMainDomLock' + foreach ($templatePath in $templatePaths) { + $a = Get-Content $templatePath -Raw | ConvertFrom-Json + if ($a.symbols -and $a.symbols.UmbracoVersion) { + $a.symbols.UmbracoVersion.defaultValue = $umbracoVersion + $a | ConvertTo-Json -Depth 32 | Set-Content $templatePath + } + } - - job: Linux_Integration_Tests_SQLServer - services: - mssql: mssql - timeoutInMinutes: 120 - displayName: Linux (SQL Server) - pool: - vmImage: ubuntu-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: '**/umbraco.sln' - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: test - projects: '**/Umbraco.Tests.Integration.csproj' - arguments: '--no-build' - env: - Tests__Database__DatabaseType: 'SqlServer' - Tests__Database__SQLServerMasterConnectionString: 'Server=localhost,1433;User Id=sa;Password=$(SA_PASSWORD);' - Umbraco__Cms__global__MainDomLock: 'SqlMainDomLock' + dotnet pack --no-build --configuration $(buildConfiguration) umbraco.sln -o $(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: macOS_Integration_Tests_SQLite - timeoutInMinutes: 120 - displayName: macOS (SQLite) - pool: - vmImage: macOS-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: '**/umbraco.sln' - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: test - projects: '**/Umbraco.Tests.Integration.csproj' - arguments: '--no-build' - env: - Tests__Database__DatabaseType: 'Sqlite' - Umbraco__Cms__global__MainDomLock: 'FileSystemMainDomLock' + - stage: Build_Docs + condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.buildApiDocs}})) + displayName: Prepare API Documentation + dependsOn: Build + variables: + umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['determineMajorVersion.majorVersion'] ] + jobs: + # C# API Reference + - job: + displayName: Build C# API Reference + pool: + vmImage: 'windows-latest' + steps: + - task: PowerShell@2 + displayName: Install DocFX + inputs: + targetType: inline + script: | + choco install docfx --version=2.59.2 -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 - - job: Windows_Integration_Tests_LocalDb - timeoutInMinutes: 120 - displayName: Windows (LocalDb) - pool: - vmImage: windows-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - powershell: sqllocaldb start mssqllocaldb - displayName: Start MSSQL LocalDb - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: '**/umbraco.sln' - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: test - projects: '**/Umbraco.Tests.Integration.csproj' - arguments: '--no-build' - env: - Tests__Database__DatabaseType: 'LocalDb' - Umbraco__Cms__global__MainDomLock: 'MainDomSemaphoreLock' + # js API Reference + - job: + displayName: Build js API Reference + pool: + vmImage: 'ubuntu-latest' + steps: + - task: NodeTool@0 + displayName: Use Node 10.15.0 + inputs: + versionSpec: 10.15.0 # Won't work with 14.18.1 + - script: | + npm ci --no-fund --no-audit --prefer-offline + npx gulp docs - - stage: Acceptance_Tests - displayName: Acceptance Tests - dependsOn: [] - variables: - - name: Umbraco__CMS__Unattended__InstallUnattended + major="$(umbracoMajorVersion)" + echo "major version: $major" + + baseUrl="https://apidocs.umbraco.com/v$major/ui/" + echo "baseUrl: $baseUrl" + + sed -i "s|baseUrl = .*|baseUrl = '$baseUrl',|" api/index.html + displayName: Generate js Docs + workingDirectory: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Docs + - task: ArchiveFiles@2 + displayName: Archive js Docs + inputs: + rootFolderOrFile: $(Build.SourcesDirectory)/src/Umbraco.Web.UI.Docs/api + includeRootFolder: false + archiveFile: $(Build.ArtifactStagingDirectory)/ui-docs.zip + - task: PublishPipelineArtifact@1 + displayName: Publish js Docs + inputs: + targetPath: $(Build.ArtifactStagingDirectory)/ui-docs.zip + artifact: ui-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: + - task: DownloadPipelineArtifact@2 + displayName: Download build artifacts + inputs: + artifact: build_output + path: $(Build.SourcesDirectory) + - task: UseDotNet@2 + condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) # net6 already on the other images + displayName: Use net6 + inputs: + version: 6.x + - task: DotNetCoreCLI@2 + displayName: Run dotnet test + inputs: + command: test + projects: '**/*.Tests.UnitTests.csproj' + arguments: '--no-build --configuration $(buildConfiguration)' + testRunTitle: Unit Tests - $(Agent.OS) + + - stage: Integration + displayName: Integration Tests + dependsOn: Build + jobs: + # Integration Tests (SQLite) + - job: + displayName: Integration Tests (SQLite) + strategy: + matrix: + Windows: + vmImage: 'windows-latest' + Linux: + vmImage: 'ubuntu-latest' + macOS: + vmImage: 'macOS-latest' + pool: + vmImage: $(vmImage) + steps: + - task: DownloadPipelineArtifact@2 + displayName: Download build artifacts + inputs: + artifact: build_output + path: $(Build.SourcesDirectory) + - task: UseDotNet@2 + displayName: Use net6 + condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) # net6 already on the other images + inputs: + version: 6.x + - task: DotNetCoreCLI@2 + displayName: Run dotnet test + inputs: + command: test + projects: '**/*.Tests.Integration.csproj' + arguments: '--no-build --configuration $(buildConfiguration)' + testRunTitle: Integration Tests SQLite - $(Agent.OS) + env: + Tests__Database__DatabaseType: 'Sqlite' + Umbraco__Cms__global__MainDomLock: 'FileSystemMainDomLock' + + # Integration Tests (SQL Server) + - job: + condition: or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.sqlServerIntegrationTests}}) + displayName: Integration Tests (SQL Server) + strategy: + matrix: + Windows: + vmImage: 'windows-latest' + testDb: LocalDb + connectionString: N/A + Linux: + vmImage: 'ubuntu-latest' + testDb: SqlServer + connectionString: 'Server=localhost,1433;User Id=sa;Password=$(SA_PASSWORD);' + pool: + vmImage: $(vmImage) + steps: + - task: DownloadPipelineArtifact@2 + displayName: Download build artifacts + inputs: + artifact: build_output + path: $(Build.SourcesDirectory) + - 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) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + - task: DotNetCoreCLI@2 + displayName: Run dotnet test + inputs: + command: test + projects: '**/*.Tests.Integration.csproj' + arguments: '--no-build --configuration $(buildConfiguration)' + testRunTitle: Integration Tests SQL Server - $(Agent.OS) + env: + Tests__Database__DatabaseType: $(testDb) + Tests__Database__SQLServerMasterConnectionString: $(connectionString) + Umbraco__Cms__global__MainDomLock: 'SqlMainDomLock' + + - stage: E2E + variables: + npm_config_cache: $(Pipeline.Workspace)/.npm_e2e + displayName: E2E Tests + dependsOn: Build + jobs: + # E2E Tests + - job: + displayName: E2E Tests + variables: + - name: Umbraco__CMS__Unattended__InstallUnattended # Windows only value: true - - name: Umbraco__CMS__Unattended__UnattendedUserName + - name: Umbraco__CMS__Unattended__UnattendedUserName # Windows only value: Cypress Test - - name: Umbraco__CMS__Unattended__UnattendedUserEmail + - name: Umbraco__CMS__Unattended__UnattendedUserEmail # Windows only value: cypress@umbraco.com - - name: Umbraco__CMS__Unattended__UnattendedUserPassword + - name: Umbraco__CMS__Unattended__UnattendedUserPassword # Windows only value: UmbracoAcceptance123! - - name: Umbraco__CMS__Global__InstallMissingDatabase + - name: Umbraco__CMS__Global__InstallMissingDatabase # Windows only value: true - jobs: - - job: Windows_Acceptance_tests - variables: - - name: UmbracoDatabaseServer - value: (LocalDB)\MSSQLLocalDB - - name: UmbracoDatabaseName - value: Cypress - - name: ConnectionStrings__umbracoDbDSN - value: Server=$(UmbracoDatabaseServer);Database=$(UmbracoDatabaseName);Integrated Security=true; - displayName: Windows - pool: - vmImage: windows-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x + - name: UmbracoDatabaseServer # Windows only + value: (LocalDB)\MSSQLLocalDB + - name: UmbracoDatabaseName # Windows only + value: Cypress + - name: ConnectionStrings__umbracoDbDSN # Windows only + value: Server=$(UmbracoDatabaseServer);Database=$(UmbracoDatabaseName);Integrated Security=true; + - name: CYPRESS_BASE_URL + value: http://localhost:8080 + strategy: + matrix: + Linux: + vmImage: 'ubuntu-latest' + dockerfile: umbraco-linux.docker + dockerImageName: umbraco-linux + cypressBinaries: "/home/vsts/.cache/Cypress" + Windows: + vmImage: 'windows-latest' + cypressBinaries: $(LOCALAPPDATA)/Cypress + pool: + vmImage: $(vmImage) + steps: + - task: DownloadPipelineArtifact@2 + displayName: Download nupkg + inputs: + artifact: nupkg + path: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/misc/nupkg + - task: NodeTool@0 + displayName: Use Node $(nodeVersion) + 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)" + path: $(npm_config_cache) + - task: PowerShell@2 + displayName: Generate Cypress.env.json + inputs: + targetType: inline + script: > + @{ username = "$(Umbraco__CMS__Unattended__UnattendedUserEmail)"; password = "$(Umbraco__CMS__Unattended__UnattendedUserPassword)" } | ConvertTo-Json | Set-Content -Path "tests/Umbraco.Tests.AcceptanceTest/cypress.env.json"# + - script: npm ci --no-fund --no-audit --prefer-offline + workingDirectory: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/ + displayName: Run npm ci + - powershell: sqllocaldb start mssqllocaldb + displayName: Start localdb (Windows only) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + - powershell: Invoke-Sqlcmd -Query "CREATE DATABASE $env:UmbracoDatabaseName" -ServerInstance $env:UmbracoDatabaseServer + displayName: Create database (Windows only) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + # Linux containers smooth + - task: PowerShell@2 + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + displayName: Build & run container (Linux only) + inputs: + workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc + targetType: inline + script: | + docker build -t $(dockerImageName) -f $(dockerfile) . + mkdir -p $(Build.ArtifactStagingDirectory)/docker-images + docker save -o $(Build.ArtifactStagingDirectory)/docker-images/$(dockerImageName).tar $(dockerImageName) + docker run --name $(dockerImageName) -dp 8080:5000 -e UMBRACO__CMS_GLOBAL__ID=$(UMBRACO__CMS_GLOBAL__ID) $(dockerImageName) + docker ps + # Windows containers take forever. + # --no-launch-profile stops ASPNETCORE_ENVIRONMENT=Development which breaks the users.ts tests (smtp config = invite user button) + # Urls matching docker setup. + - task: PowerShell@2 + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + displayName: Build & run app (Windows only) + inputs: + workingDirectory: tests/Umbraco.Tests.AcceptanceTest/misc + targetType: inline + script: | + dotnet new --install ./nupkg/Umbraco.Templates.*.nupkg + dotnet new umbraco --name Cypress -o . --no-restore + dotnet restore --configfile ./nuget.config + dotnet build --no-restore -c Release + Start-Process -FilePath "dotnet" -ArgumentList "run --no-build -c Release --no-launch-profile --urls $(CYPRESS_BASE_URL)" + - task: PowerShell@2 + displayName: Wait for app + inputs: + targetType: inline + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + script: | + npm i -g wait-on + wait-on -v --interval 1000 --timeout 120000 $(CYPRESS_BASE_URL) + - task: PowerShell@2 + displayName: Run Cypress (Desktop) + continueOnError: true + inputs: + targetType: inline + workingDirectory: tests/Umbraco.Tests.AcceptanceTest + script: 'npm run test -- --reporter junit --reporter-options "mochaFile=results/test-output-D-[hash].xml,toConsole=true" --config="viewportHeight=1600,viewportWidth=2560,screenshotsFolder=cypress/artifacts/desktop/screenshots,videosFolder=cypress/artifacts/desktop/videos,videoUploadOnPasses=false"' + - task: PublishTestResults@2 + displayName: Publish test results + condition: always() + inputs: + testResultsFormat: 'JUnit' + testResultsFiles: 'tests/Umbraco.Tests.AcceptanceTest/results/test-output-D-*.xml' + mergeTestResults: true + testRunTitle: "e2e - $(Agent.OS)" + - task: CopyFiles@2 + displayName: Prepare artifacts + condition: always() + inputs: + sourceFolder: $(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/cypress/artifacts + targetFolder: $(Build.ArtifactStagingDirectory)/cypresss + - task: PublishPipelineArtifact@1 + displayName: "Publish test artifacts" + condition: always() + inputs: + targetPath: $(Build.ArtifactStagingDirectory) + artifact: 'E2E artifacts - $(Agent.OS) - Attempt #$(System.JobAttempt)' - - powershell: sqllocaldb start mssqllocaldb - displayName: Start MSSQL LocalDb - - powershell: Invoke-Sqlcmd -Query "CREATE DATABASE $env:UmbracoDatabaseName" -ServerInstance $env:UmbracoDatabaseServer - displayName: Create database -# - task: DotNetCoreCLI@2 -# displayName: dotnet build -# inputs: -# command: build -# projects: '**/Umbraco.Web.UI.csproj' - - task: NodeTool@0 - displayName: Use Node $(nodeVersion) - inputs: - versionSpec: $(nodeVersion) - - task: Npm@1 - displayName: npm ci (Client) - inputs: - command: ci - workingDir: src\Umbraco.Web.UI.Client - verbose: false - - task: gulp@0 - displayName: gulp build - inputs: - gulpFile: src\Umbraco.Web.UI.Client\gulpfile.js - targets: build - workingDirectory: src\Umbraco.Web.UI.Client - - powershell: Start-Process -FilePath "dotnet" -ArgumentList "run", "--project", "src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" - displayName: dotnet run - - task: PowerShell@1 - displayName: Generate Cypress.env.json - inputs: - scriptType: inlineScript - inlineScript: > - @{ username = $env:Umbraco__CMS__Unattended__UnattendedUserEmail; password = $env:Umbraco__CMS__Unattended__UnattendedUserPassword } | ConvertTo-Json | Set-Content -Path "tests\Umbraco.Tests.AcceptanceTest\cypress.env.json" - - task: Npm@1 - name: PrepareTask - displayName: npm ci (AcceptanceTest) - inputs: - command: ci - workingDir: 'tests\Umbraco.Tests.AcceptanceTest' - - task: PowerShell@2 - displayName: Run Cypress (Desktop) - condition: always() - continueOnError: true - inputs: - targetType: inline - workingDirectory: tests\Umbraco.Tests.AcceptanceTest - script: 'npm run test -- --reporter junit --reporter-options "mochaFile=results/test-output-D-[hash].xml,toConsole=true" --config="viewportHeight=1600,viewportWidth=2560,screenshotsFolder=cypress/artifacts/desktop/screenshots,videosFolder=cypress/artifacts/desktop/videos,videoUploadOnPasses=false"' + ############################################### + ## Release + ############################################### + - stage: Deploy_MyGet + displayName: MyGet pre-release + dependsOn: + - Unit + - Integration + # - E2E # TODO: Enable when stable. + condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.myGetDeploy}})) + jobs: + - job: + 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' + publishFeedCredentials: 'MyGet - Pre-releases' - - task: PublishTestResults@2 - condition: always() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: 'tests/Umbraco.Tests.AcceptanceTest/results/test-output-D-*.xml' - mergeTestResults: true - testRunTitle: "Test results Desktop" -# - task: Npm@1 -# displayName: Run Cypress (Tablet portrait) -# condition: always() -# inputs: -# workingDir: tests\Umbraco.Tests.AcceptanceTest -# command: 'custom' -# customCommand: 'run test -- --config="viewportHeight=1366,viewportWidth=1024,screenshotsFolder=cypress/artifacts/tablet/screenshots,videosFolder=cypress/artifacts/tablet/videos,videoUploadOnPasses=false"' -# -# - task: Npm@1 -# displayName: Run Cypress (Mobile protrait) -# condition: always() -# inputs: -# workingDir: tests\Umbraco.Tests.AcceptanceTest -# command: 'custom' -# customCommand: 'run test -- --config="viewportHeight=812,viewportWidth=375,screenshotsFolder=cypress/artifacts/mobile/screenshots,videosFolder=cypress/artifacts/mobile/videos,videoUploadOnPasses=false"' - - task: PublishPipelineArtifact@1 - displayName: "Publish test artifacts" - inputs: - targetPath: '$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/cypress/artifacts' - artifact: 'Test artifacts - Windows - Attempt #$(System.JobAttempt)' + - stage: Deploy_NuGet + displayName: NuGet release + dependsOn: + - Deploy_MyGet + - Build_Docs + condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{parameters.nuGetDeploy}})) + jobs: + - job: + 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.*' - - job: Linux_Acceptance_tests_SqlServer - displayName: Linux (SQL Server) - variables: - - name: UmbracoDatabaseServer - value: localhost - - name: UmbracoDatabaseName - value: Cypress - - name: ConnectionStrings__umbracoDbDSN - value: Server=localhost,1433;Database=$(UmbracoDatabaseName);User Id=sa;Password=$(SA_PASSWORD); - services: - mssql: mssql - pool: - vmImage: ubuntu-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: Bash@3 - displayName: Create database - inputs: - targetType: 'inline' - script: 'sqlcmd -S . -U sa -P $SA_PASSWORD -Q "CREATE DATABASE $DBNAME"' - env: - DBNAME: $(UmbracoDatabaseName) - SA_PASSWORD: $(SA_PASSWORD) - - task: NodeTool@0 - displayName: Use Node $(nodeVersion) - inputs: - versionSpec: $(nodeVersion) - - task: Npm@1 - displayName: npm ci (Client) - inputs: - command: ci - workingDir: src/Umbraco.Web.UI.Client - verbose: false - - task: gulp@0 - displayName: gulp build - inputs: - gulpFile: src/Umbraco.Web.UI.Client/gulpfile.js - targets: build - workingDirectory: src/Umbraco.Web.UI.Client - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: src/Umbraco.Web.UI/Umbraco.Web.UI.csproj - - task: Bash@3 - displayName: dotnet run - inputs: - targetType: 'inline' - script: 'nohup dotnet run --no-build --project ./src/Umbraco.Web.UI/ > $(Build.ArtifactStagingDirectory)/dotnet_run_log_linux.txt &' - - task: Bash@3 - displayName: Generate Cypress.env.json - inputs: - targetType: 'inline' - script: 'echo "{ \"username\": \"$USERNAME\", \"password\": \"$PASSWORD\" }" > "tests/Umbraco.Tests.AcceptanceTest/cypress.env.json"' - env: - USERNAME: $(Umbraco__CMS__Unattended__UnattendedUserEmail) - PASSWORD: $(Umbraco__CMS__Unattended__UnattendedUserPassword) - - task: Npm@1 - name: PrepareTask - displayName: npm ci (AcceptanceTest) - inputs: - command: ci - workingDir: 'tests/Umbraco.Tests.AcceptanceTest' - - task: Bash@3 - displayName: Run Cypress (Desktop) - condition: always() - continueOnError: true - inputs: - targetType: inline - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - script: 'npm run test -- --reporter junit --reporter-options "mochaFile=results/test-output-D-[hash].xml,toConsole=true" --config="viewportHeight=1600,viewportWidth=2560,screenshotsFolder=cypress/artifacts/desktop/screenshots,videosFolder=cypress/artifacts/desktop/videos,videoUploadOnPasses=false"' - - task: PublishTestResults@2 - condition: always() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: 'tests/Umbraco.Tests.AcceptanceTest/results/test-output-D-*.xml' - mergeTestResults: true - testRunTitle: "Test results Desktop" - # - task: Npm@1 - # displayName: Run Cypress (Tablet portrait) - # condition: always() - # inputs: - # workingDir: tests/Umbraco.Tests.AcceptanceTest - # command: 'custom' - # customCommand: 'run test -- --config="viewportHeight=1366,viewportWidth=1024,screenshotsFolder=cypress/artifacts/tablet/screenshots,videosFolder=cypress/artifacts/tablet/videos,videoUploadOnPasses=false"' - # - # - task: Npm@1 - # displayName: Run Cypress (Mobile protrait) - # condition: always() - # inputs: - # workingDir: tests/Umbraco.Tests.AcceptanceTest - # command: 'custom' - # customCommand: 'run test -- --config="viewportHeight=812,viewportWidth=375,screenshotsFolder=cypress/artifacts/mobile/screenshots,videosFolder=cypress/artifacts/mobile/videos,videoUploadOnPasses=false"' - - task: PublishPipelineArtifact@1 - displayName: "Publish test artifacts" - inputs: - targetPath: '$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/cypress/artifacts' - artifact: 'Test artifacts - Linux (SQL Server) - Attempt #$(System.JobAttempt)' - - task: PublishPipelineArtifact@1 - displayName: "Publish run log" - inputs: - targetPath: '$(Build.ArtifactStagingDirectory)/dotnet_run_log_linux.txt' - artifact: 'Test Run logs - Linux (SQL Server) - Attempt #$(System.JobAttempt)' - - - job: Linux_Acceptance_tests_SQLite - displayName: Linux (SQLite) - variables: - - name: ConnectionStrings__umbracoDbDSN - value: Data Source=|DataDirectory|/umbraco-cms-cypress.sqlite.db;Cache=Private;Foreign Keys=True - - name: ConnectionStrings__umbracoDbDSN_ProviderName - value: Microsoft.Data.SQLite - pool: - vmImage: ubuntu-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: NodeTool@0 - displayName: Use Node $(nodeVersion) - inputs: - versionSpec: $(nodeVersion) - - task: Npm@1 - displayName: npm ci (Client) - inputs: - command: ci - workingDir: src/Umbraco.Web.UI.Client - verbose: false - - task: gulp@0 - displayName: gulp build - inputs: - gulpFile: src/Umbraco.Web.UI.Client/gulpfile.js - targets: build - workingDirectory: src/Umbraco.Web.UI.Client - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: build - projects: src/Umbraco.Web.UI/Umbraco.Web.UI.csproj - - task: Bash@3 - displayName: dotnet run - inputs: - targetType: 'inline' - script: 'nohup dotnet run --no-build -p ./src/Umbraco.Web.UI/ > $(Build.ArtifactStagingDirectory)/dotnet_run_log_linux.txt &' - - task: Bash@3 - displayName: Generate Cypress.env.json - inputs: - targetType: 'inline' - script: 'echo "{ \"username\": \"$USERNAME\", \"password\": \"$PASSWORD\" }" > "tests/Umbraco.Tests.AcceptanceTest/cypress.env.json"' - env: - USERNAME: $(Umbraco__CMS__Unattended__UnattendedUserEmail) - PASSWORD: $(Umbraco__CMS__Unattended__UnattendedUserPassword) - - task: Npm@1 - name: PrepareTask - displayName: npm ci (AcceptanceTest) - inputs: - command: ci - workingDir: 'tests/Umbraco.Tests.AcceptanceTest' - - task: Bash@3 - displayName: Run Cypress (Desktop) - condition: always() - continueOnError: true - inputs: - targetType: inline - workingDirectory: tests/Umbraco.Tests.AcceptanceTest - script: 'npm run test -- --reporter junit --reporter-options "mochaFile=results/test-output-D-[hash].xml,toConsole=true" --config="viewportHeight=1600,viewportWidth=2560,screenshotsFolder=cypress/artifacts/desktop/screenshots,videosFolder=cypress/artifacts/desktop/videos,videoUploadOnPasses=false"' - - task: PublishTestResults@2 - condition: always() - inputs: - testResultsFormat: 'JUnit' - testResultsFiles: 'tests/Umbraco.Tests.AcceptanceTest/results/test-output-D-*.xml' - mergeTestResults: true - testRunTitle: "Test results Desktop" - - - task: PublishPipelineArtifact@1 - displayName: "Publish test artifacts" - inputs: - targetPath: '$(Build.SourcesDirectory)/tests/Umbraco.Tests.AcceptanceTest/cypress/artifacts' - artifact: 'Test artifacts - Linux (SQLite) - Attempt #$(System.JobAttempt)' - - task: PublishPipelineArtifact@1 - displayName: "Publish run log" - inputs: - targetPath: '$(Build.ArtifactStagingDirectory)/dotnet_run_log_linux.txt' - artifact: 'Test Run logs - Linux (SQLite) - Attempt #$(System.JobAttempt)' - - - stage: Artifacts - dependsOn: [] - jobs: - - job: Build_Artifacts - displayName: Build Artifacts - pool: - vmImage: windows-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: NuGetToolInstaller@1 - displayName: Use NuGet Latest - - task: NuGetCommand@2 - displayName: Restore NuGet Packages - inputs: - restoreSolution: 'umbraco.sln' - feedsToUse: config - - task: PowerShell@1 - displayName: Update Version and Artifact Name - inputs: - scriptType: inlineScript - inlineScript: > - Write-Host "Working folder: $pwd" - - $ubuild = build/build.ps1 -get -continue - - $version = $ubuild.GetUmbracoVersion() - - $isRelease = [regex]::matches($env:BUILD_SOURCEBRANCH,"v\d+\/\d+.\d+.*") - - if ($isRelease.Count -gt 0) { - $continuous = $version.Semver - } else { - $date = (Get-Date).ToString("yyyyMMdd") - $continuous = "$($version.release)-preview$date.$(Build.BuildId)" - $ubuild.SetUmbracoVersion($continuous) - - # Update the default Umbraco version in templates - $templatePaths = Get-ChildItem 'templates/**/.template.config/template.json' - foreach ($templatePath in $templatePaths) { - $a = Get-Content $templatePath -Raw | ConvertFrom-Json - if ($a.symbols -and $a.symbols.UmbracoVersion) { - $a.symbols.UmbracoVersion.defaultValue = $continuous - $a | ConvertTo-Json -Depth 32 | Set-Content $templatePath - } - } - } - - Write-Host "##vso[build.updatebuildnumber]$continuous.$(Build.BuildId)" - - Write-Host "Building: $continuous" - - task: PowerShell@1 - displayName: Prepare Build - inputs: - scriptType: inlineScript - inlineScript: | - Write-Host "Working folder: $pwd" - $ubuild = build\build.ps1 -get - - $ubuild.PrepareBuild("vso") - - task: PowerShell@1 - displayName: Prepare JSON Schema - inputs: - scriptType: inlineScript - inlineScript: | - Write-Host "Working folder: $pwd" - $ubuild = build\build.ps1 -get -continue - - $ubuild.CompileJsonSchema() - - task: NodeTool@0 - displayName: Use Node $(nodeVersion) - inputs: - versionSpec: $(nodeVersion) - - task: Npm@1 - displayName: npm ci (Client) - inputs: - command: ci - workingDir: src\Umbraco.Web.UI.Client - verbose: false - - task: gulp@0 - displayName: gulp build - inputs: - gulpFile: src\Umbraco.Web.UI.Client\gulpfile.js - targets: build - workingDirectory: src\Umbraco.Web.UI.Client - publishJUnitResults: true - testResultsFiles: '**\TESTS-*.xml' - - task: PowerShell@1 - displayName: Prepare Packages - inputs: - scriptType: inlineScript - inlineScript: | - Write-Host "Working folder: $pwd" - $ubuild = build\build.ps1 -get -continue - - $ubuild.CompileUmbraco() - $ubuild.PreparePackages() - - task: PowerShell@1 - displayName: Verify & Package NuGet - inputs: - scriptType: inlineScript - inlineScript: | - Write-Host "Working folder: $pwd" - $ubuild = build\build.ps1 -get -continue - - $ubuild.VerifyNuGet() - $ubuild.PackageNuGet() - - task: CopyFiles@2 - displayName: Copy NuPkg Files to Staging - inputs: - SourceFolder: build.out - Contents: '*.*nupkg' - TargetFolder: $(build.artifactstagingdirectory) - CleanTargetFolder: true - - task: PublishBuildArtifacts@1 - displayName: Publish NuPkg Files - inputs: - PathtoPublish: $(build.artifactstagingdirectory) - ArtifactName: nupkg - - task: CopyFiles@2 - displayName: Copy Log Files to Staging - inputs: - SourceFolder: build.tmp - Contents: '*.log' - TargetFolder: $(build.artifactstagingdirectory) - CleanTargetFolder: true - condition: succeededOrFailed() - - task: PublishBuildArtifacts@1 - displayName: Publish Log Files - inputs: - PathtoPublish: $(build.artifactstagingdirectory) - ArtifactName: logs - condition: succeededOrFailed() - - stage: Artifacts_Docs - displayName: 'Static Code Documentation' - dependsOn: [Determine_build_type] - jobs: - - job: Generate_Docs_CSharp - timeoutInMinutes: 60 - displayName: Generate C# Docs - condition: eq(stageDependencies.Determine_build_type.Set_build_variables.outputs['setReleaseVariable.isRelease'], 'true') - pool: - vmImage: windows-latest - steps: - - task: UseDotNet@2 - displayName: Use .Net 6.x - inputs: - version: 6.x - - task: PowerShell@2 - displayName: 'Prep build tool - C# Docs' - inputs: - targetType: inline - script: | - choco install docfx --version=2.59.0 -y - if ($lastexitcode -ne 0){ - throw ("Error installing DocFX") - } - docfx metadata --loglevel Verbose "$(Build.SourcesDirectory)\src\ApiDocs\docfx.json" - if ($lastexitcode -ne 0){ - throw ("Error generating docs.") - } - docfx build --loglevel Verbose "$(Build.SourcesDirectory)\src\ApiDocs\docfx.json" - if ($lastexitcode -ne 0){ - throw ("Error generating docs.") - } - errorActionPreference: continue - workingDirectory: build - - task: ArchiveFiles@2 - displayName: 'Zip C# Docs' - inputs: - rootFolderOrFile: $(Build.SourcesDirectory)\src\ApiDocs\_site - includeRootFolder: false - archiveType: zip - archiveFile: $(Build.ArtifactStagingDirectory)\docs\csharp-docs.zip - replaceExistingArchive: true - - task: PublishPipelineArtifact@1 - displayName: Publish to artifacts - C# Docs - inputs: - targetPath: $(Build.ArtifactStagingDirectory)\docs\csharp-docs.zip - artifact: docs-cs - publishLocation: pipeline - - job: Generate_Docs_JS - timeoutInMinutes: 60 - displayName: Generate JS Docs - condition: eq(stageDependencies.Determine_build_type.Set_build_variables.outputs['setReleaseVariable.isRelease'], 'true') - pool: - vmImage: windows-latest - steps: - - task: PowerShell@2 - displayName: Prep build tool - JS Docs - inputs: - targetType: inline - script: | - $uenv=./build.ps1 -get -doc - $uenv.SandboxNode() - $uenv.CompileBelle() - $uenv.PrepareAngularDocs() - $uenv.RestoreNode() - errorActionPreference: continue - workingDirectory: build - - task: PublishPipelineArtifact@1 - displayName: Publish to artifacts - JS Docs - inputs: - targetPath: $(Build.Repository.LocalPath)\build.out\ - artifact: docs - publishLocation: pipeline + - stage: Upload_API_Docs + pool: + vmImage: 'windows-latest' # Apparently AzureFileCopy is windows only :( + variables: + umbracoMajorVersion: $[ stageDependencies.Build.A.outputs['determineMajorVersion.majorVersion'] ] + displayName: Upload API Documention + dependsOn: + - Build + - Deploy_NuGet + condition: and(succeeded(), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), ${{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 + - 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 + - job: + displayName: Upload js Docs + 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 + - task: AzureFileCopy@4 + displayName: 'Copy UI Docs to blob storage' + inputs: + SourcePath: '$(Build.ArtifactStagingDirectory)/ui-docs/*' + azureSubscription: umbraco-storage + Destination: AzureBlob + storage: umbracoapidocs + ContainerName: '$web' + BlobPrefix: v$(umbracoMajorVersion)/ui diff --git a/build/build-bootstrap.ps1 b/build/build-bootstrap.ps1 deleted file mode 100644 index 4c946ba289..0000000000 --- a/build/build-bootstrap.ps1 +++ /dev/null @@ -1,95 +0,0 @@ - - # this script should be dot-sourced into the build.ps1 scripts - # right after the parameters declaration - # ie - # . "$PSScriptRoot\build-bootstrap.ps1" - - # THIS FILE IS DISTRIBUTED AS PART OF UMBRACO.BUILD - # DO NOT MODIFY IT - ALWAYS USED THE COMMON VERSION - - # ################################################################ - # BOOTSTRAP - # ################################################################ - - # reset errors - $error.Clear() - - # ensure we have temp folder for downloads - $scriptRoot = "$PSScriptRoot" - $scriptTemp = "$scriptRoot\temp" - if (-not (test-path $scriptTemp)) { mkdir $scriptTemp > $null } - - # get NuGet - $cache = 4 - $nuget = "$scriptTemp\nuget.exe" - # ensure the correct NuGet-source is used. This one is used by Umbraco - $nugetsourceUmbraco = "https://www.myget.org/F/umbracoprereleases/api/v3/index.json" - if (-not $local) - { - $source = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" - if ((test-path $nuget) -and ((ls $nuget).CreationTime -lt [DateTime]::Now.AddDays(-$cache))) - { - Remove-Item $nuget -force -errorAction SilentlyContinue > $null - } - if (-not (test-path $nuget)) - { - Write-Host "Download NuGet..." - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - Invoke-WebRequest $source -OutFile $nuget - if (-not $?) { throw "Failed to download NuGet." } - } - } - elseif (-not (test-path $nuget)) - { - throw "Failed to locate NuGet.exe." - } - - # NuGet notes - # As soon as we use -ConfigFile, NuGet uses that file, and only that file, and does not - # merge configuration from system level. See comments in NuGet.Client solution, class - # NuGet.Configuration.Settings, method LoadDefaultSettings. - # For NuGet to merge configurations, it needs to "find" the file in the current directory, - # or above. Which means we cannot really use -ConfigFile but instead have to have Umbraco's - # NuGet.config file at root, and always run NuGet.exe while at root or in a directory below - # root. - - $solutionRoot = "$scriptRoot\.." - $testPwd = [System.IO.Path]::GetFullPath($pwd.Path) + "\" - $testRoot = [System.IO.Path]::GetFullPath($solutionRoot) + "\" - if (-not $testPwd.ToLower().StartsWith($testRoot.ToLower())) - { - throw "Cannot run outside of the solution's root." - } - - # get the build system - if (-not $local) - { - $params = "-OutputDirectory", $scriptTemp, "-Verbosity", "quiet", "-PreRelease", "-Source", $nugetsourceUmbraco - &$nuget install Umbraco.Build @params - if (-not $?) { throw "Failed to download Umbraco.Build." } - } - - # ensure we have the build system - $ubuildPath = ls "$scriptTemp\Umbraco.Build.*" | sort -property CreationTime -descending | select -first 1 - if (-not $ubuildPath) - { - throw "Failed to locate the build system." - } - - # boot the build system - # this creates $global:ubuild - return &"$ubuildPath\ps\Boot.ps1" - - # at that point the build.ps1 script must boot the build system - # eg - # $ubuild.Boot($ubuildPath.FullName, [System.IO.Path]::GetFullPath("$scriptRoot\.."), - # @{ Local = $local; With7Zip = $false; WithNode = $false }, - # @{ continue = $continue }) - # if (-not $?) { throw "Failed to boot the build system." } - # - # and it's good practice to report - # eg - # Write-Host "Umbraco.Whatever Build" - # Write-Host "Umbraco.Build v$($ubuild.BuildVersion)" - - # eof diff --git a/build/build.ps1 b/build/build.ps1 deleted file mode 100644 index 0c2780fb03..0000000000 --- a/build/build.ps1 +++ /dev/null @@ -1,491 +0,0 @@ - - param ( - # get, don't execute - [Parameter(Mandatory=$false)] - [Alias("g")] - [switch] $get = $false, - - # run local, don't download, assume everything is ready - [Parameter(Mandatory=$false)] - [Alias("l")] - [Alias("loc")] - [switch] $local = $false, - - # enable docfx - [Parameter(Mandatory=$false)] - [Alias("doc")] - [switch] $docfx = $false, - - # keep the build directories, don't clear them - [Parameter(Mandatory=$false)] - [Alias("c")] - [Alias("cont")] - [switch] $continue = $false, - - # execute a command - [Parameter(Mandatory=$false, ValueFromRemainingArguments=$true)] - [String[]] - $command - ) - - # ################################################################ - # BOOTSTRAP - # ################################################################ - - # create and boot the buildsystem - $ubuild = &"$PSScriptRoot\build-bootstrap.ps1" - if (-not $?) { return } - $ubuild.Boot($PSScriptRoot, - @{ Local = $local; WithDocFx = $docfx }, - @{ Continue = $continue }) - if ($ubuild.OnError()) { return } - - Write-Host "Umbraco CMS Build" - Write-Host "Umbraco.Build v$($ubuild.BuildVersion)" - - # ################################################################ - # TASKS - # ################################################################ - - $ubuild.DefineMethod("SetMoreUmbracoVersion", - { - param ( $semver ) - - $port = "" + $semver.Major + $semver.Minor + ("" + $semver.Patch).PadLeft(2, '0') - Write-Host "Update port in launchSettings.json to $port" - $filePath = "$($this.SolutionRoot)\src\Umbraco.Web.UI\Properties\launchSettings.json" - $this.ReplaceFileText($filePath, ` - "http://localhost:(\d+)?", ` - "http://localhost:$port") - }) - - $ubuild.DefineMethod("SandboxNode", - { - $global:node_path = $env:path - $nodePath = $this.BuildEnv.NodePath - $gitExe = (Get-Command git).Source - if (-not $gitExe) { $gitExe = (Get-Command git).Path } - $gitPath = [System.IO.Path]::GetDirectoryName($gitExe) - $env:path = "$nodePath;$gitPath" - - $global:node_nodepath = $this.ClearEnvVar("NODEPATH") - $global:node_npmcache = $this.ClearEnvVar("NPM_CONFIG_CACHE") - $global:node_npmprefix = $this.ClearEnvVar("NPM_CONFIG_PREFIX") - - # https://github.com/gruntjs/grunt-contrib-connect/issues/235 - $this.SetEnvVar("NODE_NO_HTTP2", "1") - }) - - $ubuild.DefineMethod("RestoreNode", - { - $env:path = $node_path - - $this.SetEnvVar("NODEPATH", $node_nodepath) - $this.SetEnvVar("NPM_CONFIG_CACHE", $node_npmcache) - $this.SetEnvVar("NPM_CONFIG_PREFIX", $node_npmprefix) - - $this.ClearEnvVar("NODE_NO_HTTP2") - }) - - $ubuild.DefineMethod("CompileBelle", - { - $src = "$($this.SolutionRoot)\src" - $log = "$($this.BuildTemp)\belle.log" - - - Write-Host "Compile Belle" - Write-Host "Logging to $log" - - # get a temp clean node env (will restore) - $this.SandboxNode() - - # stupid PS is going to gather all "warnings" in $error - # so we have to take care of it else they'll bubble and kill the build - if ($error.Count -gt 0) { return } - - try { - Push-Location "$($this.SolutionRoot)\src\Umbraco.Web.UI.Client" - Write-Output "" > $log - - Write-Output "### node version is:" > $log - node -v >> $log 2>&1 - if (-not $?) { throw "Failed to report node version." } - - Write-Output "### npm version is:" >> $log 2>&1 - npm -v >> $log 2>&1 - if (-not $?) { throw "Failed to report npm version." } - - Write-Output "### clean npm cache" >> $log 2>&1 - npm cache clean --force >> $log 2>&1 - $error.Clear() # that one can fail 'cos security bug - ignore - - Write-Output "### npm ci" >> $log 2>&1 - npm ci >> $log 2>&1 - Write-Output ">> $? $($error.Count)" >> $log 2>&1 - # Don't really care about the messages from npm ci making us think there are errors - $error.Clear() - - Write-Output "### gulp build for version $($this.Version.Release)" >> $log 2>&1 - npm run build --buildversion=$this.Version.Release >> $log 2>&1 - - # We can ignore this warning, we need to update to node 12 at some point - https://github.com/jsdom/jsdom/issues/2939 - $indexes = [System.Collections.ArrayList]::new() - $index = 0; - $error | ForEach-Object { - # Find which of the errors is the ExperimentalWarning - if($_.ToString().Contains("ExperimentalWarning: The fs.promises API is experimental")) { - [void]$indexes.Add($index) - } - $index++ - } - $indexes | ForEach-Object { - # Loop through the list of indexes and remove the errors that we expect and feel confident we can ignore - $error.Remove($error[$_]) - } - - if (-not $?) { throw "Failed to build" } # that one is expected to work - } finally { - Pop-Location - - # FIXME: should we filter the log to find errors? - #get-content .\build.tmp\belle.log | %{ if ($_ -match "build") { write $_}} - - # restore - $this.RestoreNode() - } - - # setting node_modules folder to hidden - # used to prevent VS13 from crashing on it while loading the websites project - # also makes sure aspnet compiler does not try to handle rogue files and chokes - # in VSO with Microsoft.VisualC.CppCodeProvider -related errors - # use get-item -force 'cos it might be hidden already - Write-Host "Set hidden attribute on node_modules" - $dir = Get-Item -force "$src\Umbraco.Web.UI.Client\node_modules" - $dir.Attributes = $dir.Attributes -bor ([System.IO.FileAttributes]::Hidden) - }) - - $ubuild.DefineMethod("CompileUmbraco", - { - $buildConfiguration = "Release" - - $src = "$($this.SolutionRoot)\src" - $log = "$($this.BuildTemp)\build.umbraco.log" - - Write-Host "Compile Umbraco" - Write-Host "Logging to $log" - - & dotnet build "$src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" ` - --configuration $buildConfiguration ` - --output "$($this.BuildTemp)\bin\\" ` - > $log - - # get files into WebApp\bin - & dotnet publish "$src\Umbraco.Web.UI\Umbraco.Web.UI.csproj" ` - --configuration Release --output "$($this.BuildTemp)\WebApp\bin\\" ` - > $log - - # remove extra files - $webAppBin = "$($this.BuildTemp)\WebApp\bin" - $excludeDirs = @("$($webAppBin)\refs","$($webAppBin)\runtimes","$($webAppBin)\umbraco","$($webAppBin)\wwwroot") - $excludeFiles = @("$($webAppBin)\appsettings.*","$($webAppBin)\*.deps.json","$($webAppBin)\*.exe","$($webAppBin)\*.config","$($webAppBin)\*.runtimeconfig.json") - $this.RemoveDirectory($excludeDirs) - $this.RemoveFile($excludeFiles) - - # copy rest of the files into WebApp - $excludeUmbracoDirs = @("$($this.BuildTemp)\WebApp\umbraco\lib","$($this.BuildTemp)\WebApp\umbraco\Data","$($this.BuildTemp)\WebApp\umbraco\Logs") - $this.RemoveDirectory($excludeUmbracoDirs) - $this.CopyFiles("$($this.SolutionRoot)\src\Umbraco.Web.UI\Views", "*", "$($this.BuildTemp)\WebApp\Views") - Copy-Item "$($this.SolutionRoot)\src\Umbraco.Web.UI\appsettings.json" "$($this.BuildTemp)\WebApp" - - if (-not $?) { throw "Failed to compile Umbraco.Web.UI." } - - # /p:UmbracoBuild tells the csproj that we are building from PS, not VS - }) - - $ubuild.DefineMethod("CompileJsonSchema", - { - Write-Host "Generating JSON Schema for AppSettings" - Write-Host "Logging to $($this.BuildTemp)\json.schema.log" - - ## NOTE: Need to specify the outputfile to point to the build temp folder - &dotnet run --project "$($this.SolutionRoot)\src\JsonSchema\JsonSchema.csproj" ` - -c Release > "$($this.BuildTemp)\json.schema.log" ` - -- ` - --outputFile "$($this.BuildTemp)\WebApp\umbraco\config\appsettings-schema.json" - }) - - $ubuild.DefineMethod("PrepareTests", - { - Write-Host "Prepare Tests" - - # FIXME: - idea is to avoid rebuilding everything for tests - # but because of our weird assembly versioning (with .* stuff) - # everything gets rebuilt all the time... - #Copy-Files "$tmp\bin" "." "$tmp\tests" - - # data - Write-Host "Copy data files" - if (-not (Test-Path -Path "$($this.BuildTemp)\tests\Packaging" )) - { - Write-Host "Create packaging directory" - mkdir "$($this.BuildTemp)\tests\Packaging" > $null - } - #$this.CopyFiles("$($this.SolutionRoot)\src\Umbraco.Tests\Packaging\Packages", "*", "$($this.BuildTemp)\tests\Packaging\Packages") - - # required for package install tests - if (-not (Test-Path -Path "$($this.BuildTemp)\tests\bin" )) - { - Write-Host "Create bin directory" - mkdir "$($this.BuildTemp)\tests\bin" > $null - } - }) - - $ubuild.DefineMethod("CompileTests", - { - $buildConfiguration = "Release" - $log = "$($this.BuildTemp)\msbuild.tests.log" - - Write-Host "Compile Tests" - Write-Host "Logging to $log" - - # beware of the weird double \\ at the end of paths - # see http://edgylogic.com/blog/powershell-and-external-commands-done-right/ - &dotnet msbuild "$($this.SolutionRoot)\tests\Umbraco.Tests\Umbraco.Tests.csproj" ` - -target:Build ` - -property:WarningLevel=0 ` - -property:Configuration=$buildConfiguration ` - -property:Platform=AnyCPU ` - -property:UseWPP_CopyWebApplication=True ` - -property:PipelineDependsOnBuild=False ` - -property:OutDir="$($this.BuildTemp)\tests\\" ` - -property:Verbosity=minimal ` - -property:UmbracoBuild=True ` - > $log - - if (-not $?) { throw "Failed to compile tests." } - - # /p:UmbracoBuild tells the csproj that we are building from PS - }) - - $ubuild.DefineMethod("PreparePackages", - { - Write-Host "Prepare Packages" - - $src = "$($this.SolutionRoot)\src" - $tmp = "$($this.BuildTemp)" - - # cleanup build - Write-Host "Clean build" - $this.RemoveFile("$tmp\bin\*.dll.config") - $this.RemoveFile("$tmp\WebApp\bin\*.dll.config") - - # cleanup presentation - Write-Host "Cleanup presentation" - $this.RemoveDirectory("$tmp\WebApp\umbraco.presentation") - - # create directories - Write-Host "Create directories" - mkdir "$tmp\WebApp\App_Data" > $null - #mkdir "$tmp\WebApp\Media" > $null - #mkdir "$tmp\WebApp\Views" > $null - - # copy various files - Write-Host "Copy xml documentation" - Copy-Item -force "$tmp\bin\*.xml" "$tmp\WebApp\bin" - - # offset the modified timestamps on all umbraco dlls, as WebResources - # break if date is in the future, which, due to timezone offsets can happen. - Write-Host "Offset dlls timestamps" - Get-ChildItem -r "$tmp\*.dll" | ForEach-Object { - $_.CreationTime = $_.CreationTime.AddHours(-11) - $_.LastWriteTime = $_.LastWriteTime.AddHours(-11) - } - }) - - - $ubuild.DefineMethod("PrepareBuild", - { - Write-host "Set environment" - $env:UMBRACO_VERSION=$this.Version.Semver.ToString() - $env:UMBRACO_RELEASE=$this.Version.Release - $env:UMBRACO_COMMENT=$this.Version.Comment - $env:UMBRACO_BUILD=$this.Version.Build - $env:UMBRACO_TMP="$($this.SolutionRoot)\build.tmp" - - if ($args -and $args[0] -eq "vso") - { - Write-host "Set VSO environment" - # set environment variable for VSO - # https://github.com/Microsoft/vsts-tasks/issues/375 - # https://github.com/Microsoft/vsts-tasks/blob/master/docs/authoring/commands.md - Write-Host ("##vso[task.setvariable variable=UMBRACO_VERSION;]$($this.Version.Semver.ToString())") - Write-Host ("##vso[task.setvariable variable=UMBRACO_RELEASE;]$($this.Version.Release)") - Write-Host ("##vso[task.setvariable variable=UMBRACO_COMMENT;]$($this.Version.Comment)") - Write-Host ("##vso[task.setvariable variable=UMBRACO_BUILD;]$($this.Version.Build)") - - Write-Host ("##vso[task.setvariable variable=UMBRACO_TMP;]$($this.SolutionRoot)\build.tmp") - } - }) - - $nugetsourceUmbraco = "https://api.nuget.org/v3/index.json" - - $ubuild.DefineMethod("RestoreNuGet", - { - Write-Host "Restore NuGet" - Write-Host "Logging to $($this.BuildTemp)\nuget.restore.log" - $params = "-Source", $nugetsourceUmbraco - &$this.BuildEnv.NuGet restore "$($this.SolutionRoot)\umbraco.sln" > "$($this.BuildTemp)\nuget.restore.log" @params - if (-not $?) { throw "Failed to restore NuGet packages." } - }) - - $ubuild.DefineMethod("PackageNuGet", - { - $nuspecs = "$($this.SolutionRoot)\build\NuSpecs" - $templates = "$($this.SolutionRoot)\templates" - - Write-Host "Create NuGet packages" - - &dotnet pack "$($this.SolutionRoot)\umbraco.sln" ` - --output "$($this.BuildOutput)" ` - --verbosity detailed ` - -c Release ` - -p:PackageVersion="$($this.Version.Semver.ToString())" > "$($this.BuildTemp)\pack.umbraco.log" - - &$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.nuspec" ` - -Properties BuildTmp="$($this.BuildTemp)" ` - -Version "$($this.Version.Semver.ToString())" ` - -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cms.log" - if (-not $?) { throw "Failed to pack NuGet UmbracoCms." } - - &$this.BuildEnv.NuGet Pack "$templates\Umbraco.Templates.nuspec" ` - -Properties BuildTmp="$($this.BuildTemp)" ` - -Version "$($this.Version.Semver.ToString())" ` - -NoDefaultExcludes ` - -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.templates.log" - if (-not $?) { throw "Failed to pack NuGet Umbraco.Templates." } - - # run hook - if ($this.HasMethod("PostPackageNuGet")) - { - Write-Host "Run PostPackageNuGet hook" - $this.PostPackageNuGet(); - if (-not $?) { throw "Failed to run hook." } - } - }) - - $ubuild.DefineMethod("VerifyNuGet", - { - $this.VerifyNuGetConsistency( - ("UmbracoCms"), - ("Umbraco.Core", "Umbraco.Infrastructure", "Umbraco.Web.UI", "Umbraco.Examine.Lucene", "Umbraco.PublishedCache.NuCache", "Umbraco.Web.Common", "Umbraco.Web.Website", "Umbraco.Web.BackOffice", "Umbraco.Cms.Persistence.Sqlite", "Umbraco.Cms.Persistence.SqlServer")) - if ($this.OnError()) { return } - }) - - $ubuild.DefineMethod("PrepareCSharpDocs", - { - Write-Host "Prepare C# Documentation" - - $src = "$($this.SolutionRoot)\src" - $tmp = $this.BuildTemp - $out = $this.BuildOutput - $DocFxJson = Join-Path -Path $src "\ApiDocs\docfx.json" - $DocFxSiteOutput = Join-Path -Path $tmp "\_site\*.*" - - # run DocFx - $DocFx = $this.BuildEnv.DocFx - - & $DocFx metadata $DocFxJson - & $DocFx build $DocFxJson - - # zip it - & $this.BuildEnv.Zip a -tzip -r "$out\csharp-docs.zip" $DocFxSiteOutput - }) - - $ubuild.DefineMethod("PrepareAngularDocs", - { - Write-Host "Prepare Angular Documentation" - - $src = "$($this.SolutionRoot)\src" - $out = $this.BuildOutput - - # Check if the solution has been built - if (!(Test-Path "$src\Umbraco.Web.UI.Client\node_modules")) {throw "Umbraco needs to be built before generating the Angular Docs"} - - "Moving to Umbraco.Web.UI.Docs folder" - cd $src\Umbraco.Web.UI.Docs - - "Generating the docs and waiting before executing the next commands" - & npm ci - & npx gulp docs - - Pop-Location - - # change baseUrl - $BaseUrl = "https://apidocs.umbraco.com/v9/ui/" - $IndexPath = "./api/index.html" - (Get-Content $IndexPath).replace('origin + location.href.substr(origin.length).replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath - - # zip it - & $this.BuildEnv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Docs\api\*.*" - }) - - $ubuild.DefineMethod("Build", - { - $error.Clear() - - $this.PrepareBuild() - if ($this.OnError()) { return } - $this.RestoreNuGet() - if ($this.OnError()) { return } - $this.CompileBelle() - if ($this.OnError()) { return } - $this.CompileUmbraco() - if ($this.OnError()) { return } - $this.CompileJsonSchema() - if ($this.OnError()) { return } - $this.PrepareTests() - if ($this.OnError()) { return } - $this.CompileTests() - if ($this.OnError()) { return } - # not running tests - $this.PreparePackages() - if ($this.OnError()) { return } - $this.VerifyNuGet() - if ($this.OnError()) { return } - $this.PackageNuGet() - if ($this.OnError()) { return } - $this.PostPackageHook() - if ($this.OnError()) { return } - - Write-Host "Done" - }) - - $ubuild.DefineMethod("PostPackageHook", - { - # run hook - if ($this.HasMethod("PostPackage")) - { - Write-Host "Run PostPackage hook" - $this.PostPackage(); - if (-not $?) { throw "Failed to run hook." } - } - }) - - # ################################################################ - # RUN - # ################################################################ - - # configure - $ubuild.ReleaseBranches = @( "master" ) - - # run - if (-not $get) - { - if ($command.Length -eq 0) - { - $command = @( "Build" ) - } - $ubuild.RunMethod($command); - if ($ubuild.OnError()) { return } - } - if ($get) { return $ubuild } diff --git a/src/ApiDocs/docfx.filter.yml b/build/csharp-docs/docfx.filter.yml similarity index 100% rename from src/ApiDocs/docfx.filter.yml rename to build/csharp-docs/docfx.filter.yml diff --git a/src/ApiDocs/docfx.json b/build/csharp-docs/docfx.json similarity index 84% rename from src/ApiDocs/docfx.json rename to build/csharp-docs/docfx.json index e5f6dd7410..195f2a7fc3 100644 --- a/src/ApiDocs/docfx.json +++ b/build/csharp-docs/docfx.json @@ -3,18 +3,17 @@ { "src": [ { - "src": "../", + "src": "../../src", "files": [ - "**/*.csproj", - "**/Umbraco.Infrastructure/**/*.cs" + "**/*.csproj" ], "exclude": [ "**/obj/**", "**/bin/**", "**/Umbraco.Web.csproj", - "**/Umbraco.Infrastructure.csproj", "**/Umbraco.Web.UI.csproj", - "**/**.Test**/*.csproj" + "**/Umbraco.Cms.StaticAssets.csproj", + "**/JsonSchema.csproj" ] } ], diff --git a/src/ApiDocs/index.md b/build/csharp-docs/index.md similarity index 100% rename from src/ApiDocs/index.md rename to build/csharp-docs/index.md diff --git a/src/ApiDocs/toc.yml b/build/csharp-docs/toc.yml similarity index 100% rename from src/ApiDocs/toc.yml rename to build/csharp-docs/toc.yml diff --git a/src/ApiDocs/umbracotemplate/partials/class.tmpl.partial b/build/csharp-docs/umbracotemplate/partials/class.tmpl.partial similarity index 100% rename from src/ApiDocs/umbracotemplate/partials/class.tmpl.partial rename to build/csharp-docs/umbracotemplate/partials/class.tmpl.partial diff --git a/src/ApiDocs/umbracotemplate/partials/footer.tmpl.partial b/build/csharp-docs/umbracotemplate/partials/footer.tmpl.partial similarity index 100% rename from src/ApiDocs/umbracotemplate/partials/footer.tmpl.partial rename to build/csharp-docs/umbracotemplate/partials/footer.tmpl.partial diff --git a/src/ApiDocs/umbracotemplate/partials/head.tmpl.partial b/build/csharp-docs/umbracotemplate/partials/head.tmpl.partial similarity index 100% rename from src/ApiDocs/umbracotemplate/partials/head.tmpl.partial rename to build/csharp-docs/umbracotemplate/partials/head.tmpl.partial diff --git a/src/ApiDocs/umbracotemplate/partials/namespace.tmpl.partial b/build/csharp-docs/umbracotemplate/partials/namespace.tmpl.partial similarity index 100% rename from src/ApiDocs/umbracotemplate/partials/namespace.tmpl.partial rename to build/csharp-docs/umbracotemplate/partials/namespace.tmpl.partial diff --git a/src/ApiDocs/umbracotemplate/partials/navbar.tmpl.partial b/build/csharp-docs/umbracotemplate/partials/navbar.tmpl.partial similarity index 100% rename from src/ApiDocs/umbracotemplate/partials/navbar.tmpl.partial rename to build/csharp-docs/umbracotemplate/partials/navbar.tmpl.partial diff --git a/src/ApiDocs/umbracotemplate/partials/rest.tmpl.partial b/build/csharp-docs/umbracotemplate/partials/rest.tmpl.partial similarity index 100% rename from src/ApiDocs/umbracotemplate/partials/rest.tmpl.partial rename to build/csharp-docs/umbracotemplate/partials/rest.tmpl.partial diff --git a/src/ApiDocs/umbracotemplate/styles/main.css b/build/csharp-docs/umbracotemplate/styles/main.css similarity index 100% rename from src/ApiDocs/umbracotemplate/styles/main.css rename to build/csharp-docs/umbracotemplate/styles/main.css diff --git a/src/JsonSchema/Options.cs b/src/JsonSchema/Options.cs index 83a2a8ef94..4471ee49ce 100644 --- a/src/JsonSchema/Options.cs +++ b/src/JsonSchema/Options.cs @@ -7,7 +7,7 @@ namespace JsonSchema { internal class Options { - [Option('o', "outputFile", Required = false, HelpText = "Set path of the output file.", Default = "../../../../Umbraco.Web.UI/umbraco/config/appsettings-schema.json")] + [Option('o', "outputFile", Required = false, HelpText = "Set path of the output file.", Default = "../../../../Umbraco.Web.UI/appsettings-schema.json")] public string OutputFile { get; set; } = null!; } } diff --git a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj index 8e27992ff9..e14abad8a1 100644 --- a/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj +++ b/src/Umbraco.Cms.StaticAssets/Umbraco.Cms.StaticAssets.csproj @@ -37,8 +37,6 @@ - - @@ -57,8 +55,6 @@ - - diff --git a/src/Umbraco.Cms/Umbraco.Cms.csproj b/src/Umbraco.Cms/Umbraco.Cms.csproj new file mode 100644 index 0000000000..3aea79587d --- /dev/null +++ b/src/Umbraco.Cms/Umbraco.Cms.csproj @@ -0,0 +1,45 @@ + + + net6.0 + false + Umbraco.Cms + Umbraco.Cms + Installs Umbraco CMS in your ASP.NET Core project + + + + + + + + + + + + + $(ProjectDir)appsettings-schema.json + $(ProjectDir)../JsonSchema/ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/NuSpecs/buildTransitive/Umbraco.Cms.props b/src/Umbraco.Cms/buildTransitive/Umbraco.Cms.props similarity index 100% rename from build/NuSpecs/buildTransitive/Umbraco.Cms.props rename to src/Umbraco.Cms/buildTransitive/Umbraco.Cms.props diff --git a/src/Umbraco.Cms/buildTransitive/Umbraco.Cms.targets b/src/Umbraco.Cms/buildTransitive/Umbraco.Cms.targets new file mode 100644 index 0000000000..7f2d0cb2c4 --- /dev/null +++ b/src/Umbraco.Cms/buildTransitive/Umbraco.Cms.targets @@ -0,0 +1,16 @@ + + + $(MSBuildThisFileDirectory)..\appsettings-schema.json + + + + + + + + + + + diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs index a7d7aaaded..11acf17a3b 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.PropertyEditors.Validators; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Sections; +using Umbraco.Cms.Core.Snippets; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Tour; using Umbraco.Cms.Core.Trees; @@ -90,6 +91,8 @@ namespace Umbraco.Cms.Core.DependencyInjection .Add() .Add() .Add(builder.TypeLoader.GetTypes()); + builder.PartialViewSnippets(); + builder.PartialViewMacroSnippets(); builder.DataValueReferenceFactories(); builder.PropertyValueConverters().Append(builder.TypeLoader.GetTypes()); builder.UrlSegmentProviders().Append(); @@ -200,6 +203,20 @@ namespace Umbraco.Cms.Core.DependencyInjection public static DashboardCollectionBuilder Dashboards(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); + /// + /// Gets the partial view snippets collection builder. + /// + /// The builder. + public static PartialViewSnippetCollectionBuilder? PartialViewSnippets(this IUmbracoBuilder builder) + => builder.WithCollectionBuilder(); + + /// + /// Gets the partial view macro snippets collection builder. + /// + /// The builder. + public static PartialViewMacroSnippetCollectionBuilder? PartialViewMacroSnippets(this IUmbracoBuilder builder) + => builder.WithCollectionBuilder(); + /// /// Gets the cache refreshers collection builder. /// diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 75b4bb4aec..29deed3af2 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -35,6 +35,7 @@ using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Snippets; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.Telemetry; using Umbraco.Cms.Core.Templates; diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml index ea28892f49..8717ba3bb0 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml @@ -2181,18 +2181,19 @@ Mange hilsner fra Umbraco robotten være muligt. Indholdet vil blive vist som ikke understøttet indhold. + Kan ikke redigeres fordi elementtypen ikke eksisterer. Billede Tilføj billede Opret ny Udklipsholder Indstillinger Avanceret - Skjuld indholds editoren + Skjul indholdseditoren Du har lavet ændringer til dette indhold. Er du sikker på at du vil kassere dem? Annuller oprettelse? - Error! - The ElementType of this block does not exist anymore + Fejl! + Elementtypen for denne blok eksisterer ikke længere Tilføj indhold Tilføj %0% Feltet %0% bruger editor %1% som ikke er supporteret for blokke. diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 1b93cacf01..1b7bcbf2d0 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -2714,6 +2714,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont will no longer be available and will be shown as unsupported content. + Cannot be edited cause ElementType does not exist. Thumbnail Add thumbnail Create empty diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index b1dd754861..7ebce617b5 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -2802,6 +2802,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont will no longer be available and will be shown as unsupported content. + Cannot be edited cause ElementType does not exist. Thumbnail Add thumbnail Create empty diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 6cbc06208c..baccd5dedf 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.IO; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Services @@ -10,6 +7,7 @@ namespace Umbraco.Cms.Core.Services /// public interface IFileService : IService { + [Obsolete("Please use SnippetCollection.GetPartialViewSnippetNames() or SnippetCollection.GetPartialViewMacroSnippetNames() instead. Scheduled for removal in V12.")] IEnumerable GetPartialViewSnippetNames(params string[] filterNames); void CreatePartialViewFolder(string folderPath); void CreatePartialViewMacroFolder(string folderPath); @@ -295,6 +293,7 @@ namespace Umbraco.Cms.Core.Services /// /// The name of the snippet /// + [Obsolete("Please use SnippetCollection.GetPartialViewMacroSnippetContent instead. Scheduled for removal in V12.")] string GetPartialViewMacroSnippetContent(string snippetName); /// @@ -302,6 +301,7 @@ namespace Umbraco.Cms.Core.Services /// /// The name of the snippet /// The content of the partial view. + [Obsolete("Please use SnippetCollection.GetPartialViewSnippetContent instead. Scheduled for removal in V12.")] string GetPartialViewSnippetContent(string snippetName); } } diff --git a/src/Umbraco.Core/Snippets/ISnippet.cs b/src/Umbraco.Core/Snippets/ISnippet.cs new file mode 100644 index 0000000000..67c9bf9e7f --- /dev/null +++ b/src/Umbraco.Core/Snippets/ISnippet.cs @@ -0,0 +1,18 @@ +namespace Umbraco.Cms.Core.Snippets +{ + /// + /// Defines a partial view macro snippet. + /// + public interface ISnippet + { + /// + /// Gets the name of the snippet. + /// + string Name { get; } + + /// + /// Gets the content of the snippet. + /// + string Content { get; } + } +} diff --git a/src/Umbraco.Core/Snippets/PartialViewMacroSnippetCollection.cs b/src/Umbraco.Core/Snippets/PartialViewMacroSnippetCollection.cs new file mode 100644 index 0000000000..b2fb79553c --- /dev/null +++ b/src/Umbraco.Core/Snippets/PartialViewMacroSnippetCollection.cs @@ -0,0 +1,66 @@ +using System.Text.RegularExpressions; +using Umbraco.Cms.Core.Composing; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Snippets +{ + /// + /// The collection of partial view macro snippets. + /// + public class PartialViewMacroSnippetCollection : BuilderCollectionBase + { + public PartialViewMacroSnippetCollection(Func> items) : base(items) + { + } + + /// + /// Gets the partial view macro snippet names. + /// + /// The names of all partial view macro snippets. + public IEnumerable GetNames() + { + var snippetNames = this.Select(x => Path.GetFileNameWithoutExtension(x.Name)).ToArray(); + + // Ensure the ones that are called 'Empty' are at the top + var empty = snippetNames.Where(x => Path.GetFileName(x)?.InvariantStartsWith("Empty") ?? false) + .OrderBy(x => x?.Length).ToArray(); + + return empty.Union(snippetNames.Except(empty)).WhereNotNull(); + } + + /// + /// Gets the content of a partial view macro snippet as a string. + /// + /// The name of the snippet. + /// The content of the partial view macro. + public string GetContentFromName(string snippetName) + { + if (snippetName.IsNullOrWhiteSpace()) + { + throw new ArgumentNullException(nameof(snippetName)); + } + + string partialViewMacroHeader = "@inherits Umbraco.Cms.Web.Common.Macros.PartialViewMacroPage"; + + var snippet = this.Where(x => x.Name.Equals(snippetName + ".cshtml")).FirstOrDefault(); + + // Try and get the snippet path + if (snippet is null) + { + throw new InvalidOperationException("Could not load snippet with name " + snippetName); + } + + // Strip the @inherits if it's there + var snippetContent = StripPartialViewHeader(snippet.Content); + + var content = $"{partialViewMacroHeader}{Environment.NewLine}{snippetContent}"; + return content; + } + + private string StripPartialViewHeader(string contents) + { + var headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline); + return headerMatch.Replace(contents, string.Empty); + } + } +} diff --git a/src/Umbraco.Core/Snippets/PartialViewMacroSnippetCollectionBuilder.cs b/src/Umbraco.Core/Snippets/PartialViewMacroSnippetCollectionBuilder.cs new file mode 100644 index 0000000000..cf737368ce --- /dev/null +++ b/src/Umbraco.Core/Snippets/PartialViewMacroSnippetCollectionBuilder.cs @@ -0,0 +1,56 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Extensions; + +namespace Umbraco.Cms.Core.Snippets +{ + /// + /// The partial view macro snippet collection builder. + /// + public class PartialViewMacroSnippetCollectionBuilder : LazyCollectionBuilderBase + { + protected override PartialViewMacroSnippetCollectionBuilder This => this; + + protected override IEnumerable CreateItems(IServiceProvider factory) + { + var hostEnvironment = factory.GetRequiredService(); + + var embeddedSnippets = new List(base.CreateItems(factory)); + var snippetProvider = new EmbeddedFileProvider(typeof(IAssemblyProvider).Assembly, "Umbraco.Cms.Core.EmbeddedResources.Snippets"); + var embeddedFiles = snippetProvider.GetDirectoryContents(string.Empty) + .Where(x => !x.IsDirectory && x.Name.EndsWith(".cshtml")); + + foreach (var file in embeddedFiles) + { + using var stream = new StreamReader(file.CreateReadStream()); + embeddedSnippets.Add(new Snippet(file.Name, stream.ReadToEnd().Trim())); + } + + var customSnippetsDir = new DirectoryInfo(hostEnvironment.MapPathContentRoot($"{Constants.SystemDirectories.Umbraco}/PartialViewMacros/Templates")); + if (!customSnippetsDir.Exists) + { + return embeddedSnippets; + } + + var customSnippets = customSnippetsDir.GetFiles().Select(f => new Snippet(f.Name, File.ReadAllText(f.FullName))); + var allSnippets = Merge(embeddedSnippets, customSnippets); + + return allSnippets; + } + + private IEnumerable Merge(IEnumerable embeddedSnippets, IEnumerable customSnippets) + { + var allSnippets = embeddedSnippets.Concat(customSnippets); + + var duplicates = allSnippets.GroupBy(s => s.Name) + .Where(gr => gr.Count() > 1) // Finds the snippets with the same name + .Select(s => s.First()); // Takes the first element from a grouping, which is the embeded snippet with that same name, + // since the physical snippet files are placed after the embedded ones in the all snippets colleciton + + // Remove any embedded snippets if a physical file with the same name can be found + return allSnippets.Except(duplicates); + } + } +} diff --git a/src/Umbraco.Core/Snippets/PartialViewSnippetCollection.cs b/src/Umbraco.Core/Snippets/PartialViewSnippetCollection.cs new file mode 100644 index 0000000000..5a0cda96e9 --- /dev/null +++ b/src/Umbraco.Core/Snippets/PartialViewSnippetCollection.cs @@ -0,0 +1,70 @@ +using System.Text.RegularExpressions; +using Umbraco.Cms.Core.Composing; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Snippets +{ + /// + /// The collection of partial view snippets. + /// + public class PartialViewSnippetCollection : BuilderCollectionBase + { + public PartialViewSnippetCollection(Func> items) : base(items) + { + } + + /// + /// Gets the partial view snippet names. + /// + /// The names of all partial view snippets. + public IEnumerable GetNames() + { + var snippetNames = this.Select(x => Path.GetFileNameWithoutExtension(x.Name)).ToArray(); + + // Ensure the ones that are called 'Empty' are at the top + var empty = snippetNames.Where(x => Path.GetFileName(x)?.InvariantStartsWith("Empty") ?? false) + .OrderBy(x => x?.Length).ToArray(); + + return empty.Union(snippetNames.Except(empty)).WhereNotNull(); + } + + /// + /// Gets the content of a partial view snippet as a string. + /// + /// The name of the snippet. + /// The content of the partial view. + public string GetContentFromName(string snippetName) + { + if (snippetName.IsNullOrWhiteSpace()) + { + throw new ArgumentNullException(nameof(snippetName)); + } + + string partialViewHeader = "@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage"; + + var snippet = this.Where(x => x.Name.Equals(snippetName + ".cshtml")).FirstOrDefault(); + + // Try and get the snippet path + if (snippet is null) + { + throw new InvalidOperationException("Could not load snippet with name " + snippetName); + } + + var snippetContent = CleanUpContents(snippet.Content); + + var content = $"{partialViewHeader}{Environment.NewLine}{snippetContent}"; + return content; + } + + private string CleanUpContents(string content) + { + // Strip the @inherits if it's there + var headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline); + var newContent = headerMatch.Replace(content, string.Empty); + + return newContent + .Replace("Model.Content.", "Model.") + .Replace("(Model.Content)", "(Model)"); + } + } +} diff --git a/src/Umbraco.Core/Snippets/PartialViewSnippetCollectionBuilder.cs b/src/Umbraco.Core/Snippets/PartialViewSnippetCollectionBuilder.cs new file mode 100644 index 0000000000..730e984d33 --- /dev/null +++ b/src/Umbraco.Core/Snippets/PartialViewSnippetCollectionBuilder.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.FileProviders; +using Umbraco.Cms.Core.Composing; + +namespace Umbraco.Cms.Core.Snippets +{ + /// + /// The partial view snippet collection builder. + /// + public class PartialViewSnippetCollectionBuilder : LazyCollectionBuilderBase + { + protected override PartialViewSnippetCollectionBuilder This => this; + + protected override IEnumerable CreateItems(IServiceProvider factory) + { + var embeddedSnippets = new List(base.CreateItems(factory)); + + // Ignore these + var filterNames = new List + { + "Gallery", + "ListChildPagesFromChangeableSource", + "ListChildPagesOrderedByProperty", + "ListImagesFromMediaFolder" + }; + + var snippetProvider = new EmbeddedFileProvider(typeof(IAssemblyProvider).Assembly, "Umbraco.Cms.Core.EmbeddedResources.Snippets"); + var embeddedFiles = snippetProvider.GetDirectoryContents(string.Empty) + .Where(x => !x.IsDirectory && x.Name.EndsWith(".cshtml")); + + foreach (var file in embeddedFiles) + { + if (!filterNames.Contains(Path.GetFileNameWithoutExtension(file.Name))) + { + using var stream = new StreamReader(file.CreateReadStream()); + embeddedSnippets.Add(new Snippet(file.Name, stream.ReadToEnd().Trim())); + } + } + + return embeddedSnippets; + } + } +} diff --git a/src/Umbraco.Core/Snippets/Snippet.cs b/src/Umbraco.Core/Snippets/Snippet.cs new file mode 100644 index 0000000000..bcb03b6d11 --- /dev/null +++ b/src/Umbraco.Core/Snippets/Snippet.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Cms.Core.Snippets +{ + public class Snippet : ISnippet + { + public string Name { get; } + public string Content { get; } + + public Snippet(string name, string content) + { + Name = name; + Content = content; + } + } +} diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index adde7948ef..c304f4e99a 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -50,6 +50,7 @@ + diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs index ee4c1dac1f..460f5158c5 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs @@ -31,7 +31,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource var templateId = PrimitiveSerializer.Int32.ReadFrom(stream); var properties = _dictionaryOfPropertyDataSerializer?.ReadFrom(stream); // TODO: We don't want to allocate empty arrays var cultureInfos = s_defaultCultureVariationsSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays - return new ContentData(name, urlSegment, versionId, versionDate, writerId, templateId, published, properties, cultureInfos); + var cachedTemplateId = templateId == 0 ? (int?)null : templateId; + return new ContentData(name, urlSegment, versionId, versionDate, writerId, cachedTemplateId, published, properties, cultureInfos); } public void WriteTo(ContentData value, Stream stream) diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs index 70d48ec000..da8da40824 100644 --- a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs +++ b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs @@ -830,7 +830,7 @@ AND cmsContentNu.nodeId IS NULL dto.VersionId, dto.EditVersionDate, dto.EditWriterId, - dto.EditTemplateId, + dto.EditTemplateId == 0 ? null : dto.EditTemplateId, published, deserializedContent?.PropertyData, deserializedContent?.CultureData); @@ -859,7 +859,7 @@ AND cmsContentNu.nodeId IS NULL dto.VersionId, dto.PubVersionDate, dto.PubWriterId, - dto.PubTemplateId, + dto.PubTemplateId == 0 ? null : dto.PubTemplateId, published, deserializedContent?.PropertyData, deserializedContent?.CultureData); diff --git a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs index a302edc56b..fbcfe283ea 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; @@ -15,6 +12,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Snippets; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Strings.Css; using Umbraco.Cms.Web.BackOffice.Filters; @@ -22,6 +20,7 @@ using Umbraco.Cms.Web.BackOffice.Trees; using Umbraco.Cms.Web.Common.ActionsResults; using Umbraco.Cms.Web.Common.Attributes; using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; using Stylesheet = Umbraco.Cms.Core.Models.Stylesheet; @@ -45,7 +44,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly IUmbracoMapper _umbracoMapper; private readonly IShortStringHelper _shortStringHelper; private readonly GlobalSettings _globalSettings; + private readonly PartialViewSnippetCollection _partialViewSnippetCollection; + private readonly PartialViewMacroSnippetCollection _partialViewMacroSnippetCollection; + [ActivatorUtilitiesConstructor] public CodeFileController( IHostingEnvironment hostingEnvironment, FileSystems fileSystems, @@ -54,7 +56,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers ILocalizedTextService localizedTextService, IUmbracoMapper umbracoMapper, IShortStringHelper shortStringHelper, - IOptionsSnapshot globalSettings) + IOptionsSnapshot globalSettings, + PartialViewSnippetCollection partialViewSnippetCollection, + PartialViewMacroSnippetCollection partialViewMacroSnippetCollection) { _hostingEnvironment = hostingEnvironment; _fileSystems = fileSystems; @@ -64,6 +68,31 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _umbracoMapper = umbracoMapper; _shortStringHelper = shortStringHelper; _globalSettings = globalSettings.Value; + _partialViewSnippetCollection = partialViewSnippetCollection; + _partialViewMacroSnippetCollection = partialViewMacroSnippetCollection; + } + + [Obsolete("Use ctor will all params. Scheduled for removal in V12.")] + public CodeFileController( + IHostingEnvironment hostingEnvironment, + FileSystems fileSystems, + IFileService fileService, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + ILocalizedTextService localizedTextService, + IUmbracoMapper umbracoMapper, + IShortStringHelper shortStringHelper, + IOptionsSnapshot globalSettings) : this( + hostingEnvironment, + fileSystems, + fileService, + backOfficeSecurityAccessor, + localizedTextService, + umbracoMapper, + shortStringHelper, + globalSettings, + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) + { } /// @@ -272,15 +301,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers switch (type) { case Constants.Trees.PartialViews: - snippets = _fileService.GetPartialViewSnippetNames( - //ignore these - (this is taken from the logic in "PartialView.ascx.cs") - "Gallery", - "ListChildPagesFromChangeableSource", - "ListChildPagesOrderedByProperty", - "ListImagesFromMediaFolder"); + snippets = _partialViewSnippetCollection.GetNames(); break; case Constants.Trees.PartialViewMacros: - snippets = _fileService.GetPartialViewSnippetNames(); + snippets = _partialViewMacroSnippetCollection.GetNames(); break; default: return NotFound(); @@ -312,7 +336,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers codeFileDisplay.VirtualPath = Constants.SystemDirectories.PartialViews; if (snippetName.IsNullOrWhiteSpace() == false) { - codeFileDisplay.Content = _fileService.GetPartialViewSnippetContent(snippetName!); + codeFileDisplay.Content = _partialViewSnippetCollection.GetContentFromName(snippetName!); } } @@ -324,7 +348,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers codeFileDisplay.VirtualPath = Constants.SystemDirectories.MacroPartials; if (snippetName.IsNullOrWhiteSpace() == false) { - codeFileDisplay.Content = _fileService.GetPartialViewMacroSnippetContent(snippetName!); + codeFileDisplay.Content = _partialViewMacroSnippetCollection.GetContentFromName(snippetName!); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs index 303ad44423..5f41682ee4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs @@ -131,7 +131,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var user = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; _cookieManager.SetCookieValue(Constants.Web.PreviewCookieName, "preview"); - return null; + return new EmptyResult(); } public ActionResult End(string? redir = null) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js index b146f471e0..cb95e60e29 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js @@ -165,13 +165,11 @@ When building a custom infinite editor view you can use the same components as a function editorService(eventsService, keyboardService, $timeout) { - let editorsKeyboardShorcuts = []; var editors = []; var isEnabled = true; var lastElementInFocus = null; - // events for backdrop eventsService.on("appState.backdrop", function (name, args) { if (args.show === true) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js index 113b26d74c..6e05712ad2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js @@ -10,7 +10,7 @@ function overlayService(eventsService, backdropService, focusLockService) { - var currentOverlay = null; + let currentOverlay = null; /** * @ngdoc method diff --git a/src/Umbraco.Web.UI.Client/src/less/components/editor.less b/src/Umbraco.Web.UI.Client/src/less/components/editor.less index 4f71fe46da..211e4d4f34 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/editor.less @@ -202,17 +202,13 @@ input.umb-editor-header__name-input:disabled { bottom: 0; } -.umb-editor-container.-stop-scrolling { - overflow: hidden; -} - -.umb-editor-actions{ +.umb-editor-actions { list-style: none; margin: 0; padding: 0; -} -.umb-editor-actions li{ - display: inline-block; + li { + display: inline-block; + } } // editor footer diff --git a/src/Umbraco.Web.UI.Client/src/less/navs.less b/src/Umbraco.Web.UI.Client/src/less/navs.less index 3d5f5e40f5..db18d3f9d2 100644 --- a/src/Umbraco.Web.UI.Client/src/less/navs.less +++ b/src/Umbraco.Web.UI.Client/src/less/navs.less @@ -266,19 +266,19 @@ .dropdown-menu > li > button { position: relative; - background: transparent; - border: 0; - padding: 8px 20px; - color: @ui-option-type; - display: flex; - justify-content: start; - clear: both; - font-weight: normal; - line-height: 20px; - white-space: nowrap; - cursor:pointer; - width: 100%; - text-align: left; + background: transparent; + border: 0; + padding: 8px 20px; + color: @ui-option-type; + display: flex; + justify-content: flex-start; + clear: both; + font-weight: normal; + line-height: 20px; + white-space: nowrap; + cursor:pointer; + width: 100%; + text-align: left; } .dropdown-menu > li > a:hover, diff --git a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card-grid.less b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card-grid.less index 58794461df..470eb3173d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card-grid.less +++ b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umb-block-card-grid.less @@ -1,15 +1,6 @@ .umb-block-card-grid { - /* FlexBox Fallback */ - display: flex; - flex-wrap: wrap; - > * { - flex: 1 1 240px; - } - - /* Grid Setup */ display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: minmax(160px, auto); - grid-gap: 20px; - + gap: 20px; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html index 9e675b15ba..aa1765f365 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-container.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js index 5e73755593..f0b89a095f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.controller.js @@ -20,7 +20,7 @@ var unsubscribe = []; - var vm = this; + const vm = this; vm.openBlock = null; function onInit() { @@ -30,11 +30,10 @@ } loadElementTypes(); - } function loadElementTypes() { - return elementTypeResource.getAll().then(function (elementTypes) { + return elementTypeResource.getAll().then(elementTypes => { vm.elementTypes = elementTypes; }); } @@ -47,24 +46,32 @@ } } } + unsubscribe.push(eventsService.on("editors.documentType.saved", updateUsedElementTypes)); - vm.requestRemoveBlockByIndex = function (index) { - localizationService.localizeMany(["general_delete", "blockEditor_confirmDeleteBlockTypeMessage", "blockEditor_confirmDeleteBlockTypeNotice"]).then(function (data) { + vm.requestRemoveBlockByIndex = function (index, event) { + + const labelKeys = [ + "general_delete", + "blockEditor_confirmDeleteBlockTypeMessage", + "blockEditor_confirmDeleteBlockTypeNotice" + ]; + + localizationService.localizeMany(labelKeys).then(data => { var contentElementType = vm.getElementTypeByKey($scope.model.value[index].contentElementTypeKey); overlayService.confirmDelete({ title: data[0], content: localizationService.tokenReplace(data[1], [contentElementType ? contentElementType.name : "(Unavailable ElementType)"]), confirmMessage: data[2], - close: function () { - overlayService.close(); - }, - submit: function () { + submit: () => { vm.removeBlockByIndex(index); overlayService.close(); - } + }, + close: overlayService.close() }); }); + + event.stopPropagation(); } vm.removeBlockByIndex = function (index) { @@ -78,7 +85,6 @@ placeholder: 'umb-block-card --sortable-placeholder' }; - vm.getAvailableElementTypes = function () { return vm.elementTypes.filter(function (type) { return !$scope.model.value.find(function (entry) { @@ -89,15 +95,13 @@ vm.getElementTypeByKey = function(key) { if (vm.elementTypes) { - return vm.elementTypes.find(function (type) { - return type.key === key; - }) || null; + return vm.elementTypes.find(type => type.key === key) || null; } }; vm.openAddDialog = function () { - localizationService.localize("blockEditor_headlineCreateBlock").then(function(localizedTitle) { + localizationService.localize("blockEditor_headlineCreateBlock").then(localizedTitle => { const contentTypePicker = { title: localizedTitle, @@ -108,10 +112,9 @@ filter: function (node) { if (node.metaData.isElement === true) { var key = udiService.getKey(node.udi); + // If a Block with this ElementType as content already exists, we will emit it as a posible option. - return $scope.model.value.find(function (entry) { - return key === entry.contentElementTypeKey; - }); + return $scope.model.value.find(entry => entry.contentElementTypeKey === key); } return true; }, @@ -138,8 +141,8 @@ } ] }; + editorService.treePicker(contentTypePicker); - }); }; @@ -151,9 +154,10 @@ isElement: true, noTemplate: true, submit: function (model) { - loadElementTypes().then( function () { + loadElementTypes().then(() => { callback(model.documentTypeKey); }); + editorService.close(); }, close: function () { @@ -165,60 +169,66 @@ vm.addBlockFromElementTypeKey = function(key) { - var blockType = { - "contentElementTypeKey": key, - "settingsElementTypeKey": null, - "labelTemplate": "", - "view": null, - "stylesheet": null, - "editorSize": "medium", - "iconColor": null, - "backgroundColor": null, - "thumbnail": null + const blockType = { + contentElementTypeKey: key, + settingsElementTypeKey: null, + labelTemplate: "", + view: null, + stylesheet: null, + editorSize: "medium", + iconColor: null, + backgroundColor: null, + thumbnail: null }; $scope.model.value.push(blockType); vm.openBlockOverlay(blockType); - }; - - - - vm.openBlockOverlay = function (block) { var elementType = vm.getElementTypeByKey(block.contentElementTypeKey); - if(elementType) { - localizationService.localize("blockEditor_blockConfigurationOverlayTitle", [elementType.name]).then(function (data) { + if (elementType) { + + let clonedBlockData = Utilities.copy(block); + vm.openBlock = block; - var clonedBlockData = Utilities.copy(block); - vm.openBlock = block; - - var overlayModel = { - block: clonedBlockData, - title: data, - view: "views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html", - size: "small", - submit: function(overlayModel) { - loadElementTypes()// lets load elementType again, to ensure we are up to date. - TransferProperties(overlayModel.block, block);// transfer properties back to block object. (Doing this cause we dont know if block object is added to model jet, therefor we cant use index or replace the object.) - overlayModel.close(); - }, - close: function() { - editorService.close(); - vm.openBlock = null; - } - }; + const overlayModel = { + block: clonedBlockData, + + view: "views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.html", + size: "small", + submit: function(overlayModel) { + loadElementTypes()// lets load elementType again, to ensure we are up to date. + TransferProperties(overlayModel.block, block);// transfer properties back to block object. (Doing this cause we dont know if block object is added to model jet, therefor we cant use index or replace the object.) + overlayModel.close(); + }, + close: function() { + editorService.close(); + vm.openBlock = null; + } + }; + localizationService.localize("blockEditor_blockConfigurationOverlayTitle", [elementType.name]).then(data => { + overlayModel.title = data, + // open property settings editor editorService.open(overlayModel); - }); } else { - alert("Cannot be edited cause ElementType does not exist."); + + const overlay = { + close: () => { + overlayService.close() + } + }; + + localizationService.localize("blockEditor_elementTypeDoesNotExist").then(data => { + overlay.content = data; + overlayService.open(overlay); + }); } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html index de6a409415..3172219434 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.html @@ -1,7 +1,7 @@
- +
-
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less index 878f6a8ef8..afb4316ce8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.less @@ -18,11 +18,11 @@ padding: 5px 15px; box-sizing: border-box; font-weight: bold; - } - - .__add-button:hover { - color: @ui-action-discreet-type-hover; - border-color: @ui-action-discreet-border-hover; + + &:hover { + color: @ui-action-discreet-type-hover; + border-color: @ui-action-discreet-border-hover; + } } } diff --git a/src/Umbraco.Web.UI.Docs/gulpfile.js b/src/Umbraco.Web.UI.Docs/gulpfile.js index 1aff4f7e35..2117b7a176 100644 --- a/src/Umbraco.Web.UI.Docs/gulpfile.js +++ b/src/Umbraco.Web.UI.Docs/gulpfile.js @@ -18,7 +18,7 @@ gulp.task('docs', [], function (cb) { var options = { html5Mode: false, startPage: '/api', - title: "Umbraco 9 Backoffice UI API Documentation", + title: "Umbraco 10 Backoffice UI API Documentation", dest: './api', styles: ['./umb-docs.css'], image: "https://our.umbraco.com/assets/images/logo.svg" diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 88b76d1af0..54ab69b37e 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -5,8 +5,6 @@ Umbraco.Cms.Web.UI - - bin/Release/Umbraco.Web.UI.xml @@ -14,23 +12,10 @@ true - + + - - - - - - - - - - - - - - - + @@ -56,7 +41,8 @@ - $(ProjectDir)umbraco/config/appsettings-schema.json + $(ProjectDir)appsettings-schema.json + $(ProjectDir)../JsonSchema/JsonSchema.csproj @@ -73,7 +59,8 @@ - + + diff --git a/src/Umbraco.Web.UI/appsettings.Development.template.json b/src/Umbraco.Web.UI/appsettings.Development.template.json index 9cac2c3dd9..33c5dd2fae 100644 --- a/src/Umbraco.Web.UI/appsettings.Development.template.json +++ b/src/Umbraco.Web.UI/appsettings.Development.template.json @@ -1,5 +1,5 @@ { - "$schema" : "./umbraco/config/appsettings-schema.json", + "$schema" : "./appsettings-schema.json", "Serilog": { "MinimumLevel": { "Default": "Information", diff --git a/src/Umbraco.Web.UI/appsettings.template.json b/src/Umbraco.Web.UI/appsettings.template.json index 2a291c5bc6..ed03407c0b 100644 --- a/src/Umbraco.Web.UI/appsettings.template.json +++ b/src/Umbraco.Web.UI/appsettings.template.json @@ -1,5 +1,5 @@ { - "$schema": "./umbraco/config/appsettings-schema.json", + "$schema": "./appsettings-schema.json", "ConnectionStrings": { "umbracoDbDSN": "" }, diff --git a/templates/Umbraco.Templates.csproj b/templates/Umbraco.Templates.csproj new file mode 100644 index 0000000000..182b220167 --- /dev/null +++ b/templates/Umbraco.Templates.csproj @@ -0,0 +1,47 @@ + + + + + + net6.0 + Template + false + . + true + true + false + . + true + false + + + + + + + + UmbracoProject\Program.cs + UmbracoProject + + + UmbracoProject\Startup.cs + UmbracoProject + + + UmbracoProject\Views\Partials\blocklist\%(RecursiveDir)%(Filename)%(Extension) + UmbracoProject\Views\Partials\blocklist + + + UmbracoProject\Views\Partials\grid\%(RecursiveDir)%(Filename)%(Extension) + UmbracoProject\Views\Partials\grid + + + UmbracoProject\Views\_ViewImports.cshtml + UmbracoProject\Views + + + UmbracoProject\wwwroot\favicon.ico + UmbracoProject\wwwroot + + + diff --git a/templates/Umbraco.Templates.nuspec b/templates/Umbraco.Templates.nuspec deleted file mode 100644 index 7539d3a1f0..0000000000 --- a/templates/Umbraco.Templates.nuspec +++ /dev/null @@ -1,32 +0,0 @@ - - - - Umbraco.Templates - 1.0.0 - Umbraco HQ - Umbraco HQ - MIT - https://umbraco.com/ - icon.png - https://umbraco.com/dist/nuget/logo-small.png - false - Umbraco CMS templates for .NET Core Template Engine available through the dotnet CLI's new command - en-US - umbraco - - - - - - - - - - - - - - - - - diff --git a/templates/UmbracoProject/appsettings.Development.json b/templates/UmbracoProject/appsettings.Development.json index 7567335326..d789f6cd32 100644 --- a/templates/UmbracoProject/appsettings.Development.json +++ b/templates/UmbracoProject/appsettings.Development.json @@ -1,5 +1,5 @@ { - "$schema": "./umbraco/config/appsettings-schema.json", + "$schema": "./appsettings-schema.json", "Serilog": { "MinimumLevel": { "Default": "Information" diff --git a/templates/UmbracoProject/appsettings.json b/templates/UmbracoProject/appsettings.json index 121e907020..5e866a77a3 100644 --- a/templates/UmbracoProject/appsettings.json +++ b/templates/UmbracoProject/appsettings.json @@ -1,5 +1,5 @@ { - "$schema": "./umbraco/config/appsettings-schema.json", + "$schema": "./appsettings-schema.json", "Serilog": { "MinimumLevel": { "Default": "Information", diff --git a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Packages/packages.ts b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Packages/packages.ts index f6ecdfc9f8..a83bbcd68f 100644 --- a/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Packages/packages.ts +++ b/tests/Umbraco.Tests.AcceptanceTest/cypress/integration/Packages/packages.ts @@ -34,7 +34,7 @@ context('Packages', () => { mediaUdis: [], mediaLoadChildNodes: false } - const url = "https://localhost:44331/umbraco/backoffice/umbracoapi/package/PostSavePackage"; + const url = "/umbraco/backoffice/umbracoapi/package/PostSavePackage"; cy.umbracoApiRequest(url, 'POST', newPackage); } @@ -159,4 +159,4 @@ context('Packages', () => { cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); cy.umbracoEnsurePackageNameNotExists(packageName); }); -}); \ No newline at end of file +}); diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/Directory.Build.props b/tests/Umbraco.Tests.AcceptanceTest/misc/Directory.Build.props new file mode 100644 index 0000000000..65a0b30da2 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/misc/Directory.Build.props @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/nuget.config b/tests/Umbraco.Tests.AcceptanceTest/misc/nuget.config new file mode 100644 index 0000000000..794b7dace7 --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/misc/nuget.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker new file mode 100644 index 0000000000..b44e817fec --- /dev/null +++ b/tests/Umbraco.Tests.AcceptanceTest/misc/umbraco-linux.docker @@ -0,0 +1,34 @@ +############################################ +## Build +############################################ + +FROM mcr.microsoft.com/dotnet/sdk:6.0.300 AS build + +WORKDIR /nupkg +COPY nupkg . + +WORKDIR /build +RUN dotnet new --install /nupkg/Umbraco.Templates.*.nupkg +RUN dotnet new umbraco --name Cypress -o . --no-restore +RUN dotnet restore -s /nupkg -s https://api.nuget.org/v3/index.json +RUN dotnet publish --no-restore --configuration Release -o /dist + +############################################ +## Run +############################################ + +FROM mcr.microsoft.com/dotnet/aspnet:6.0.5 AS run + +WORKDIR /cypress +COPY --from=build dist . + +ENV ASPNETCORE_URLS="http://0.0.0.0:5000" +ENV Umbraco__CMS__Global__InstallMissingDatabase="true" +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="Cypress Test" +ENV Umbraco__CMS__Unattended__UnattendedUserEmail="cypress@umbraco.com" +ENV Umbraco__CMS__Unattended__UnattendedUserPassword="UmbracoAcceptance123!" + +CMD dotnet Cypress.dll diff --git a/tests/Umbraco.Tests.Common/Extensions/StringExtensions.cs b/tests/Umbraco.Tests.Common/Extensions/StringExtensions.cs new file mode 100644 index 0000000000..b5ce0b6a88 --- /dev/null +++ b/tests/Umbraco.Tests.Common/Extensions/StringExtensions.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Cms.Tests.Common.Extensions; + +public static class StringExtensions +{ + public static string StripNewLines(this string input) => + input.Replace("\r\n", string.Empty) + .Replace("\n", string.Empty); + + public static string NormalizeNewLines(this string input) => + input.Replace("\r\n", "\n"); +} diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 3bf6907aa8..1954093f66 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -7,6 +7,7 @@ Umbraco CMS Integration Tests Contains helper classes for integration tests with Umbraco, including all internal integration tests. true + true diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/StylesheetHelperTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/StylesheetHelperTests.cs index f918251566..736e6c76a1 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/StylesheetHelperTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/StylesheetHelperTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Umbraco.Cms.Core.Strings.Css; +using Umbraco.Cms.Tests.Common.Extensions; using Umbraco.Extensions; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.ShortStringHelper @@ -175,7 +176,7 @@ world */p{font-size: 1em;}")] /**umb_name:Test2*/ .test2 { #font-color: green; -}"), result); +}").NormalizeNewLines(), result.NormalizeNewLines()); } [Test] @@ -204,7 +205,7 @@ world */p{font-size: 1em;}")] Assert.AreEqual(".test", rules.First().Selector); Assert.AreEqual( @"font-color: red; -margin: 1rem;", rules.First().Styles); +margin: 1rem;".NormalizeNewLines(), rules.First().Styles.NormalizeNewLines()); Assert.AreEqual("Test2", rules.Last().Name); Assert.AreEqual(".test2", rules.Last().Selector); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs index a0d3c9b9eb..2b65cf81ef 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.PublishedCache.NuCache/SnapDictionaryTests.cs @@ -564,12 +564,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.PublishedCache.NuCache GC.Collect(); await d.CollectAsync(); - // in Release mode, it works, but in Debug mode, the weak reference is still alive - // and for some reason we need to do this to ensure it is collected -#if DEBUG GC.Collect(); await d.CollectAsync(); -#endif Assert.AreEqual(1, d.SnapCount); v2 = s2.Get(1); @@ -611,12 +607,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.PublishedCache.NuCache GC.Collect(); await d.CollectAsync(); - // in Release mode, it works, but in Debug mode, the weak reference is still alive - // and for some reason we need to do this to ensure it is collected -#if DEBUG GC.Collect(); await d.CollectAsync(); -#endif Assert.AreEqual(1, d.SnapCount); v2 = s2.Get(1); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index cce774aa04..70bf694128 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -5,6 +5,7 @@ net6.0 Umbraco.Cms.Tests.UnitTests false + true diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Macros/MacroParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Macros/MacroParserTests.cs index 7cfa722957..5e8ff67962 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Macros/MacroParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Macros/MacroParserTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using NUnit.Framework; using Umbraco.Cms.Infrastructure.Macros; +using Umbraco.Cms.Tests.Common.Extensions; using Umbraco.Cms.Tests.Common.TestHelpers; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros @@ -35,8 +36,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros
Macro alias: My.Map.isCool eh[boy!]
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -54,8 +55,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros
Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -73,8 +74,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros
Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -92,8 +93,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros
Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -111,8 +112,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros
Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -130,8 +131,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros
Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -161,8 +162,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros
Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -186,8 +187,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Macros
Macro alias: login
-

 

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

 

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -223,8 +224,8 @@ dfdsfds"" /> asdfsdf
-".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +".StripNewLines(), + result.StripNewLines()); } [Test] @@ -242,8 +243,8 @@ asdfsdf
Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -261,8 +262,8 @@ asdfsdf
Macro alias: Map
-

asdfasdf

".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +

asdfasdf

".StripNewLines(), + result.StripNewLines()); } [Test] @@ -318,8 +319,8 @@ asdfsdf asdfsdf
-".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +".StripNewLines(), + result.StripNewLines()); } [Test] @@ -353,8 +354,8 @@ asdfsdf asdfsdf
-".Replace(Environment.NewLine, string.Empty), - result.Replace(Environment.NewLine, string.Empty)); +".StripNewLines(), + result.StripNewLines()); } [Test] diff --git a/umbraco.sln b/umbraco.sln index 79b2a3f85a..2ea2faa7cc 100644 --- a/umbraco.sln +++ b/umbraco.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29209.152 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32328.378 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Web.UI", "src\Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{DCDFE97C-5630-4F6F-855D-8AEEB96556A5}" EndProject @@ -26,11 +26,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{FD962632-1 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B5BD12C1-A454-435E-8A46-FF4A364C0382}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{227C3B55-80E5-4E7E-A802-BE16C5128B9D}" - ProjectSection(SolutionItems) = preProject - build\NuSpecs\UmbracoCms.nuspec = build\NuSpecs\UmbracoCms.nuspec - EndProjectSection -EndProject Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.Client", "http://localhost:3961", "{3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}" ProjectSection(WebsiteProperties) = preProject UseIISExpress = "true" @@ -124,12 +119,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.Sql EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.SqlServer", "src\Umbraco.Cms.Persistence.SqlServer\Umbraco.Cms.Persistence.SqlServer.csproj", "{93C5910D-2E36-475D-88EB-A11BA5B50F65}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Cms.StaticAssets", "src\Umbraco.Cms.StaticAssets\Umbraco.Cms.StaticAssets.csproj", "{D2FD54E8-3470-4A98-8B0C-A9ACB59BED48}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.StaticAssets", "src\Umbraco.Cms.StaticAssets\Umbraco.Cms.StaticAssets.csproj", "{D2FD54E8-3470-4A98-8B0C-A9ACB59BED48}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "buildTransitive", "buildTransitive", "{A05481CB-A335-4BB7-9A86-1BBB3874F742}" - ProjectSection(SolutionItems) = preProject - build\NuSpecs\buildTransitive\Umbraco.Cms.props = build\NuSpecs\buildTransitive\Umbraco.Cms.props - EndProjectSection +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Templates", "Templates", "{6D72A60B-0542-4AA9-A493-DD4179E838A1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Templates", "templates\Umbraco.Templates.csproj", "{05C1D0C8-C592-468F-AF8F-A299B9B3A903}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms", "src\Umbraco.Cms\Umbraco.Cms.csproj", "{92EAA57A-CC99-4F5D-9D9C-B865293F6000}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -241,12 +237,23 @@ Global {D2FD54E8-3470-4A98-8B0C-A9ACB59BED48}.Release|Any CPU.Build.0 = Release|Any CPU {D2FD54E8-3470-4A98-8B0C-A9ACB59BED48}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU {D2FD54E8-3470-4A98-8B0C-A9ACB59BED48}.SkipTests|Any CPU.Build.0 = Debug|Any CPU + {05C1D0C8-C592-468F-AF8F-A299B9B3A903}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05C1D0C8-C592-468F-AF8F-A299B9B3A903}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05C1D0C8-C592-468F-AF8F-A299B9B3A903}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05C1D0C8-C592-468F-AF8F-A299B9B3A903}.Release|Any CPU.Build.0 = Release|Any CPU + {05C1D0C8-C592-468F-AF8F-A299B9B3A903}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU + {05C1D0C8-C592-468F-AF8F-A299B9B3A903}.SkipTests|Any CPU.Build.0 = Debug|Any CPU + {92EAA57A-CC99-4F5D-9D9C-B865293F6000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92EAA57A-CC99-4F5D-9D9C-B865293F6000}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92EAA57A-CC99-4F5D-9D9C-B865293F6000}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92EAA57A-CC99-4F5D-9D9C-B865293F6000}.Release|Any CPU.Build.0 = Release|Any CPU + {92EAA57A-CC99-4F5D-9D9C-B865293F6000}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU + {92EAA57A-CC99-4F5D-9D9C-B865293F6000}.SkipTests|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {227C3B55-80E5-4E7E-A802-BE16C5128B9D} = {2849E9D4-3B4E-40A3-A309-F3CB4F0E125F} {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {53594E5B-64A2-4545-8367-E3627D266AE8} = {FD962632-184C-4005-A5F3-E705D92FC645} {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} @@ -255,7 +262,7 @@ Global {D6319409-777A-4BD0-93ED-B2DFD805B32C} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {A499779C-1B3B-48A8-B551-458E582E6E96} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {9102ABDF-E537-4E46-B525-C9ED4833EED0} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} - {A05481CB-A335-4BB7-9A86-1BBB3874F742} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} + {05C1D0C8-C592-468F-AF8F-A299B9B3A903} = {6D72A60B-0542-4AA9-A493-DD4179E838A1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC} diff --git a/version.json b/version.json new file mode 100644 index 0000000000..3afe65e7b4 --- /dev/null +++ b/version.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "10.0.0-rc2", + "assemblyVersion": { + "precision": "Build" // optional. Use when you want a more precise assembly version than the default major.minor. + }, + "gitCommitIdShortFixedLength": 7, + "publicReleaseRefSpec": [ + "^refs/heads/master$", + "^refs/heads/main$", + "^refs/heads/release/\\d+\\.\\d+\\.\\d+" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true + } + }, + "nugetPackageVersion": { + "semVer": 2 + } +}