diff --git a/Directory.Build.props b/Directory.Build.props index b6219c8d..944bec49 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,6 +7,7 @@ <_DefaultTargetFrameworks>net8.0-android + <_DefaultNetTargetFrameworks>net8.0 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7a1e5433..cca877cd 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,24 +5,19 @@ trigger: pr: - main -variables: - BUILD_NUMBER: $(Build.BuildNumber) - BUILD_COMMIT: $(Build.SourceVersion) - - # Build variables - mainBranchName: main # Name of Git "main" branch - configuration: Release # Build configuration: 'Debug', 'Release' - - # Reporting variables - areaPath: DevDiv\VS Client - Runtime SDKs\Android # AzDo area path to log any issues - - # Windows specific variables - windowsAgentPoolName: Maui-1ESPT # Windows VM pool name - windowsImage: 1ESPT-Windows2022 # Windows VM image name +parameters: +- name: RunExtendedTests + displayName: Run Extended Tests? + type: boolean + default: false - # macOS specific variables - macosAgentPoolName: Azure Pipelines # macOS VM pool name - macosImage: internal-macos12 # macOS VM image name +variables: + # Variables used by both AndroidX/GPS go in the template + - template: build/ci/variables.yml@self + + # Variables only used by AndroidX go here + - name: skipUnitTests + value: false resources: repositories: @@ -45,7 +40,8 @@ extends: os: windows stages: - - stage: Build + - stage: build_windows + displayName: Build - Windows jobs: - template: build/ci/build.yml@self @@ -55,10 +51,20 @@ extends: name: $(windowsAgentPoolName) image: $(windowsImage) os: windows - mainBranchName: $(mainBranchName) - configuration: $(configuration) runAPIScan: true + - template: sign-artifacts/jobs/v2.yml@internal-templates + parameters: + artifactName: output-windows + usePipelineArtifactTasks: true + use1ESTemplate: true + condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/') + + - stage: build_mac + displayName: Build - Mac + dependsOn: + + jobs: - template: build/ci/build.yml@self parameters: name: macos @@ -66,13 +72,11 @@ extends: name: $(macosAgentPoolName) vmImage: $(macosImage) os: macOS - mainBranchName: $(mainBranchName) - configuration: $(configuration) - - template: sign-artifacts/jobs/v2.yml@internal-templates - parameters: - dependsOn: [ 'build_windows' ] - artifactName: output-windows - usePipelineArtifactTasks: true - use1ESTemplate: true - condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/') + - template: build/ci/stage-extended-tests.yml@self + parameters: + stageCondition: and(succeeded(), eq('${{ parameters.RunExtendedTests }}', 'true')) + buildPool: + name: $(windowsAgentPoolName) + image: $(windowsImage) + os: windows diff --git a/build/ci/build-and-test.yml b/build/ci/build-and-test.yml index c6fa3ae5..9a2f87da 100644 --- a/build/ci/build-and-test.yml +++ b/build/ci/build-and-test.yml @@ -1,9 +1,5 @@ parameters: - condition: succeeded() - verbosity: # the build verbosity: 'minimal', 'normal', 'diagnostic' - configuration: # the build configuration: 'Debug', 'Release' artifactsPath: - skipUnitTests: false # do not run unit test step validPackagePrefixes: # any NuGet prefixes that should pass validation - Xamarin @@ -14,8 +10,8 @@ steps: - pwsh: | dotnet cake build.cake ` --target=ci-build ` - --configuration="${{ parameters.configuration }}" ` - --verbosity="${{ parameters.verbosity }}" + --configuration="$(configuration)" ` + --verbosity="$(verbosity)" displayName: 'Build packages' env: JavaSdkDirectory: $(JAVA_HOME) @@ -27,7 +23,7 @@ steps: - pwsh: | dotnet cake validation.cake ` --namespaces="${{ join(',', parameters.validPackagePrefixes) }}" ` - --verbosity="${{ parameters.verbosity }}" + --verbosity="$(verbosity)" displayName: 'Run NuGet package validation' - pwsh: | @@ -35,7 +31,7 @@ steps: --artifacts="${{ parameters.artifactsPath }}" ` --output="${{ parameters.artifactsPath }}/api-diff" ` --cache="$(Agent.TempDirectory)/api-diff" ` - --verbosity="${{ parameters.verbosity }}" + --verbosity="$(verbosity)" displayName: 'Generate API diff' - pwsh: dotnet cake utilities.cake -t=verify-namespace-file @@ -44,8 +40,8 @@ steps: - pwsh: | dotnet cake build.cake ` --target=ci-samples ` - --configuration="${{ parameters.configuration }}" ` - --verbosity="${{ parameters.verbosity }}" + --configuration="$(configuration)" ` + --verbosity="$(verbosity)" displayName: 'Build samples' env: JavaSdkDirectory: $(JAVA_HOME) @@ -56,8 +52,8 @@ steps: - task: DotNetCoreCLI@2 displayName: Run unit tests - condition: ne(${{ parameters.skipUnitTests }}, 'true') + condition: ne(variables['skipUnitTests'], 'true') inputs: command: test projects: util/**/*.Tests.csproj - arguments: '-c ${{ parameters.configuration }}' + arguments: '-c $(configuration)' diff --git a/build/ci/build.yml b/build/ci/build.yml index 58c66d22..209752eb 100644 --- a/build/ci/build.yml +++ b/build/ci/build.yml @@ -4,19 +4,9 @@ parameters: buildPool: # VM pool information # Build Parameters - mainBranchName: 'main' # Name of Git "main" branch - configuration: 'Release' # Build configuration: 'Debug', 'Release' - verbosity: 'normal' # Build verbosity: 'minimal', 'normal', 'diagnostic' timeoutInMinutes: 300 # Max job runtime in minutes runAPIScan: false # Run APIScan analysis - # Tool Parameters - dotnetVersion: '8.0.301' # .NET version to install on agent - dotnetWorkloadRollbackFile: 'workloads.json' # Rollback file specifying workload versions to install - dotnetNuGetOrgSource: 'https://api.nuget.org/v3/index.json' # NuGet.org URL to find workloads - dotnetWorkloadSource: 'https://aka.ms/dotnet6/nuget/index.json' # .NET engineering URL to find workloads - skipUnitTests: false # Skip running unit tests - tools: # Additional .NET global tools to install - 'xamarin.androidbinderator.tool': '0.5.7' - 'Cake.Tool': '4.0.0' @@ -47,23 +37,16 @@ jobs: steps: - template: setup-environment.yml parameters: - dotnetVersion: ${{ parameters.dotnetVersion }} - dotnetWorkloadRollbackFile: ${{ parameters.dotnetWorkloadRollbackFile }} - dotnetWorkloadSource: ${{ parameters.dotnetWorkloadSource }} - dotnetNuGetOrgSource: ${{ parameters.dotnetNuGetOrgSource }} dotnetTools: ${{ parameters.tools }} - template: build-and-test.yml parameters: artifactsPath: ${{ parameters.artifactsPath }} - verbosity: ${{ parameters.verbosity }} - configuration: ${{ parameters.configuration }} - skipUnitTests: ${{ parameters.skipUnitTests }} - ${{ if eq(parameters.runAPIScan, true) }}: - template: api-scan.yml parameters: - mainBranchName: ${{ parameters.mainBranchName }} + mainBranchName: $(mainBranchName) # Copy SignList.xml to output - pwsh: | diff --git a/build/ci/job-extended-tests.yml b/build/ci/job-extended-tests.yml new file mode 100644 index 00000000..f3b24eb0 --- /dev/null +++ b/build/ci/job-extended-tests.yml @@ -0,0 +1,61 @@ +# Runs test(s) that are too expensive to run on every commit + +parameters: + jobName: # Job display name ('Android' or 'MAUI') + buildPool: # VM pool information + agentCount: # Agents to run in parallel + testFilter: # Test category filter + + tools: # Additional .NET global tools to install + - 'dotnet-test-slicer' : '0.1.0-alpha7' + +jobs: +- job: ${{ parameters.jobName }}_package_tests + displayName: ${{ parameters.jobName }} Package Tests + strategy: + parallel: ${{ parameters.agentCount }} + pool: ${{ parameters.buildPool }} + timeoutInMinutes: 480 + + steps: + - template: setup-environment.yml + parameters: + dotnetTools: ${{ parameters.tools }} + + - task: DownloadPipelineArtifact@2 + inputs: + artifactName: output-windows + downloadPath: output + + # Build test assembly + - task: DotNetCoreCLI@2 + displayName: Build unit tests + inputs: + command: build + projects: $(testAssemblyProject) + arguments: -c $(configuration) + + # Figure out which tests this slice is running + - pwsh: >- + dotnet-test-slicer + slice + --test-assembly="$(testAssembly)" + --test-filter="${{ parameters.testFilter }}" + --slice-number=$(System.JobPositionInPhase) + --total-slices=$(System.TotalJobsInPhase) + --outfile="$(testAssembly).runsettings" + displayName: Slice unit tests + failOnStderr: true + + # Run unit tests + - task: DotNetCoreCLI@2 + inputs: + command: test + projects: $(testAssembly) + arguments: >- + --settings "$(testAssembly).runsettings" + publishTestResults: true + testRunTitle: ${{ parameters.jobName }} Package Tests - $(System.JobPositionInPhase) + displayName: Run unit tests + continueOnError: true + timeoutInMinutes: 480 diff --git a/build/ci/setup-environment.yml b/build/ci/setup-environment.yml index 4dcf5b67..8838c419 100644 --- a/build/ci/setup-environment.yml +++ b/build/ci/setup-environment.yml @@ -1,23 +1,18 @@ parameters: - condition: succeeded() - dotnetVersion: '' - dotnetWorkloadRollbackFile: '' - dotnetWorkloadSource: '' - dotnetNuGetOrgSource: '' dotnetTools: [] steps: # before the build starts, make sure the tooling is as expected - task: UseDotNet@2 - displayName: 'Use dotnet ${{ parameters.dotnetVersion }}' + displayName: 'Use dotnet $(dotnetVersion)' inputs: - version: ${{ parameters.dotnetVersion }} + version: $(dotnetVersion) performMultiLevelLookup: true includePreviewVersions: true - condition: ne('${{ parameters.dotnetVersion }}', '') + condition: ne('$(dotnetVersion)', '') - pwsh: | - dotnet workload install maui --verbosity diag --from-rollback-file ${{ parameters.dotnetWorkloadRollbackFile }} --source ${{ parameters.dotnetWorkloadSource }} --source ${{ parameters.dotnetNuGetOrgSource }} + dotnet workload install maui --verbosity diag --from-rollback-file $(dotnetWorkloadRollbackFile) --source $(dotnetWorkloadSource) --source $(dotnetNuGetOrgSource) if ($LASTEXITCODE -ne 0) { Write-Host "##vso[task.logissue type=error]Failed to install workloads." Write-Host "##vso[task.complete result=Failed;]" diff --git a/build/ci/stage-extended-tests.yml b/build/ci/stage-extended-tests.yml new file mode 100644 index 00000000..df8a8030 --- /dev/null +++ b/build/ci/stage-extended-tests.yml @@ -0,0 +1,27 @@ +# Runs test(s) that are too expensive to run on every commit + +parameters: + stageCondition: # When to run this stage + buildPool: # VM pool information + +stages: +- stage: extended_tests + displayName: Extended Tests + dependsOn: build_windows + condition: ${{ parameters.stageCondition }} + + jobs: + + - template: job-extended-tests.yml + parameters: + jobName: Android + agentCount: 6 + testFilter: cat = Android + buildPool: ${{ parameters.buildPool }} + + - template: job-extended-tests.yml + parameters: + jobName: MAUI + agentCount: 10 + testFilter: cat = MAUI + buildPool: ${{ parameters.buildPool }} diff --git a/build/ci/variables.yml b/build/ci/variables.yml new file mode 100644 index 00000000..97ef9b78 --- /dev/null +++ b/build/ci/variables.yml @@ -0,0 +1,30 @@ +variables: + + BUILD_NUMBER: $(Build.BuildNumber) + BUILD_COMMIT: $(Build.SourceVersion) + + # Build variables + mainBranchName: main # Name of Git "main" branch + configuration: Release # Build configuration: 'Debug', 'Release' + verbosity: 'normal' # Build verbosity: 'minimal', 'normal', 'diagnostic' + + # Reporting variables + areaPath: DevDiv\VS Client - Runtime SDKs\Android # AzDo area path to log any issues + + # Windows specific variables + windowsAgentPoolName: Maui-1ESPT # Windows VM pool name + windowsImage: 1ESPT-Windows2022 # Windows VM image name + + # macOS specific variables + macosAgentPoolName: Azure Pipelines # macOS VM pool name + macosImage: internal-macos12 # macOS VM image name + + # Tool variables + dotnetVersion: '8.0.301' # .NET version to install on agent + dotnetWorkloadRollbackFile: 'workloads.json' # Rollback file specifying workload versions to install + dotnetNuGetOrgSource: 'https://api.nuget.org/v3/index.json' # NuGet.org URL to find workloads + dotnetWorkloadSource: 'https://aka.ms/dotnet6/nuget/index.json' # .NET engineering URL to find workloads + + # Extended test variables + testAssemblyProject: tests/extended/ExtendedTests.csproj # Extended tests project file + testAssembly: tests/extended/bin/$(configuration)/net8.0/ExtendedTests.dll # Extended tests compiled binary diff --git a/samples/NuGet.config b/samples/NuGet.config index 836ca00f..7eb3b451 100644 --- a/samples/NuGet.config +++ b/samples/NuGet.config @@ -21,11 +21,20 @@ + + + + + - + + + + + diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props new file mode 100644 index 00000000..d8a473f1 --- /dev/null +++ b/tests/Directory.Build.props @@ -0,0 +1,9 @@ + + + + + <_DefaultTargetFrameworks>net8.0-android + <_DefaultNetTargetFrameworks>net8.0 + + diff --git a/tests/extended/BinderatorConfigFileParser.cs b/tests/extended/BinderatorConfigFileParser.cs new file mode 100644 index 00000000..2681c156 --- /dev/null +++ b/tests/extended/BinderatorConfigFileParser.cs @@ -0,0 +1,195 @@ +#nullable disable + +using System.ComponentModel; +using Newtonsoft.Json; + +namespace ExtendedTests; + +public class BinderatorConfigFileParser +{ + public static async Task> ParseConfigurationFile (string filename) + { + string json; + + if (filename.StartsWith ("http")) { + + // Configuration file URL + using (var client = new HttpClient ()) + json = await client.GetStringAsync (filename); + + } else { + + // Local configuration file + if (string.IsNullOrWhiteSpace (filename) || !File.Exists (filename)) { + System.Console.WriteLine ($"Could not find configuration file: '{filename}'"); + Environment.Exit (-1); + } + + json = File.ReadAllText (filename); + } + + return JsonConvert.DeserializeObject> (json); + } + + static async Task> GetExternalDependencies (Options options) + { + var list = new List (); + + foreach (var file in options.DependencyConfigs) { + var config = await ParseConfigurationFile (file); + + list.AddRange (config.SelectMany (arr => arr.Artifacts.Where (a => !a.DependencyOnly))); + } + + return list; + } + + // Configuration File Model + public class Template + { + [JsonProperty ("templateFile")] + public string TemplateFile { get; set; } + + [JsonProperty ("outputFileRule")] + public string OutputFileRule { get; set; } + } + + public class TemplateSetModel + { + [JsonProperty ("name")] + public string Name { get; set; } + + [JsonProperty ("mavenRepositoryType")] + public MavenRepoType? MavenRepositoryType { get; set; } + + [JsonProperty ("mavenRepositoryLocation")] + public string MavenRepositoryLocation { get; set; } = null; + + [JsonProperty ("templates")] + public List