From 21b88c4da882a4680e5195db4f92c2923bd5be26 Mon Sep 17 00:00:00 2001 From: Matous Kozak Date: Mon, 7 Sep 2026 15:21:42 +0200 Subject: [PATCH 1/6] Use fallback feed for MAUI NuGet config pinning Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/scenarios/shared/mauisharedpython.py | 48 +++++++++++++----------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/scenarios/shared/mauisharedpython.py b/src/scenarios/shared/mauisharedpython.py index f43ac960f00..4ab318f6551 100644 --- a/src/scenarios/shared/mauisharedpython.py +++ b/src/scenarios/shared/mauisharedpython.py @@ -202,6 +202,23 @@ def _find_latest_manifest_package(packages: list[dict], workload_name: str) -> d return packages[0] +def _resolve_manifest_package(precommands: PreCommands, workload_name: str, feed_url: str) -> tuple[dict, str]: + '''Resolve the latest manifest package and the feed that supplies its metadata.''' + try: + packages = precommands.get_packages_for_sdk_from_feed(workload_name, feed_url) + except Exception as e: + getLogger().warning(f"Failed to get packages for {workload_name} from latest feed: {e}") + getLogger().info("Trying second latest feed as fallback") + feed_url = extract_latest_dotnet_feed_from_nuget_config( + path=os.path.join(get_repo_root_path(), "NuGet.config"), + offset=1 + ) + getLogger().info(f"Using fallback feed: {feed_url}") + packages = precommands.get_packages_for_sdk_from_feed(workload_name, feed_url) + + getLogger().debug(f"All package IDs for {workload_name}: {[pkg['id'] for pkg in packages]}") + return _find_latest_manifest_package(packages, workload_name), feed_url + def _get_nuget_flat_container_base(feed_url: str) -> Optional[str]: ''' Download the NuGet V3 service index and return the PackageBaseAddress (flat container) base URL. @@ -292,21 +309,23 @@ def _discover_repo_commits(precommands: PreCommands, feed_url: str) -> dict[str, repo_commits: dict[str, str] = {} cached_packages: dict[str, dict] = getattr(precommands, '_cached_manifest_packages', {}) - - flat_base = _get_nuget_flat_container_base(feed_url) - if flat_base is None: - getLogger().warning("Cannot discover repo commits: failed to get flat container base URL") - return repo_commits + flat_bases: dict[str, Optional[str]] = {} for repo, workload_name in REPO_TO_PROBE_WORKLOAD.items(): try: getLogger().info(f"Discovering commit SHA for {repo} via {workload_name} package...") - packages = precommands.get_packages_for_sdk_from_feed(workload_name, feed_url) - manifest_pkg = _find_latest_manifest_package(packages, workload_name) + manifest_pkg, manifest_feed = _resolve_manifest_package(precommands, workload_name, feed_url) # Cache the resolved package so install_latest_maui can reuse it cached_packages[workload_name] = manifest_pkg + + if manifest_feed not in flat_bases: + flat_bases[manifest_feed] = _get_nuget_flat_container_base(manifest_feed) + flat_base = flat_bases[manifest_feed] + if flat_base is None: + getLogger().warning(f"Cannot discover commit SHA for {repo}: failed to get flat container base URL from {manifest_feed}") + continue result = _get_commit_sha_from_nuspec(flat_base, manifest_pkg['id'], manifest_pkg['latestVersion']) if result is None: @@ -656,20 +675,7 @@ def install_latest_maui( latest_package = cached_packages[workload] getLogger().info(f"Using cached manifest package for {workload}: {latest_package['id']} v{latest_package['latestVersion']}") else: - try: - packages = precommands.get_packages_for_sdk_from_feed(workload, feed) - except Exception as e: - getLogger().warning(f"Failed to get packages for {workload} from latest feed: {e}") - getLogger().info("Trying second latest feed as fallback") - fallback_feed = extract_latest_dotnet_feed_from_nuget_config( - path=os.path.join(get_repo_root_path(), "NuGet.config"), - offset=1 - ) - getLogger().info(f"Using fallback feed: {fallback_feed}") - packages = precommands.get_packages_for_sdk_from_feed(workload, fallback_feed) - - getLogger().debug(f"All package IDs for {workload}: {[pkg['id'] for pkg in packages]}") - latest_package = _find_latest_manifest_package(packages, workload) + latest_package, _ = _resolve_manifest_package(precommands, workload, feed) getLogger().info(f"Latest package details for {workload}: ID={latest_package['id']}, Version={latest_package['latestVersion']}, SDK_Version={latest_package['sdk_version']}, .NET_Version={latest_package['dotnet_version']}") From 8376c7e2bdf9b891f31afaa5ba6a0966ef17e2e7 Mon Sep 17 00:00:00 2001 From: Matous Kozak Date: Mon, 7 Sep 2026 15:21:43 +0200 Subject: [PATCH 2/6] Limit internal workload validation to Android and iOS Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- azure-pipelines.yml | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4cd188a6a74..4fa0ba4363d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,15 +55,37 @@ schedules: always: true jobs: - - template: /eng/pipelines/sdk-perf-jobs.yml + # Test branch: run one configuration per mobile platform. + - template: /eng/pipelines/templates/build-machine-matrix.yml parameters: - ${{ if or(eq(variables['System.TeamProject'], 'public'), parameters.runPublicJobs) }}: - runPublicJobs: true - ${{ if ne(variables['System.TeamProject'], 'public') }}: - ${{ if or(notin(variables['Build.Reason'], 'PullRequest', 'Manual'), parameters.runPrivateJobs) }}: - runPrivateJobs: true - ${{ if or(eq(variables['Build.CronSchedule.DisplayName'], 'Every 12 hours build'), parameters.runScheduledPrivateJobs) }}: - runScheduledPrivateJobs: true + jobTemplate: /eng/pipelines/templates/run-scenarios-job.yml + buildMachines: + - win-x64-android-arm64-pixel + isPublic: false jobParameters: - ${{ if parameters.onlySanityCheck }}: - onlySanityCheck: true + runKind: maui_scenarios_android + projectFileName: maui_scenarios_android.proj + channels: + - main + runtimeFlavor: coreclr + codeGenType: Default + buildConfig: Release + additionalJobIdentifier: CoreCLR + onlySanityCheck: ${{ parameters.onlySanityCheck }} + + - template: /eng/pipelines/templates/build-machine-matrix.yml + parameters: + jobTemplate: /eng/pipelines/templates/run-scenarios-job.yml + buildMachines: + - osx-x64-ios-arm64 + isPublic: false + jobParameters: + runKind: maui_scenarios_ios + projectFileName: maui_scenarios_ios.proj + channels: + - main + runtimeFlavor: coreclr + codeGenType: Default + buildConfig: Release + additionalJobIdentifier: CoreCLR + onlySanityCheck: ${{ parameters.onlySanityCheck }} From c05ac1fb5f414632b29657897491f575d0ae0663 Mon Sep 17 00:00:00 2001 From: Matous Kozak Date: Mon, 7 Sep 2026 16:18:56 +0200 Subject: [PATCH 3/6] Restore full pipeline after workload validation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- azure-pipelines.yml | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4fa0ba4363d..4cd188a6a74 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,37 +55,15 @@ schedules: always: true jobs: - # Test branch: run one configuration per mobile platform. - - template: /eng/pipelines/templates/build-machine-matrix.yml + - template: /eng/pipelines/sdk-perf-jobs.yml parameters: - jobTemplate: /eng/pipelines/templates/run-scenarios-job.yml - buildMachines: - - win-x64-android-arm64-pixel - isPublic: false + ${{ if or(eq(variables['System.TeamProject'], 'public'), parameters.runPublicJobs) }}: + runPublicJobs: true + ${{ if ne(variables['System.TeamProject'], 'public') }}: + ${{ if or(notin(variables['Build.Reason'], 'PullRequest', 'Manual'), parameters.runPrivateJobs) }}: + runPrivateJobs: true + ${{ if or(eq(variables['Build.CronSchedule.DisplayName'], 'Every 12 hours build'), parameters.runScheduledPrivateJobs) }}: + runScheduledPrivateJobs: true jobParameters: - runKind: maui_scenarios_android - projectFileName: maui_scenarios_android.proj - channels: - - main - runtimeFlavor: coreclr - codeGenType: Default - buildConfig: Release - additionalJobIdentifier: CoreCLR - onlySanityCheck: ${{ parameters.onlySanityCheck }} - - - template: /eng/pipelines/templates/build-machine-matrix.yml - parameters: - jobTemplate: /eng/pipelines/templates/run-scenarios-job.yml - buildMachines: - - osx-x64-ios-arm64 - isPublic: false - jobParameters: - runKind: maui_scenarios_ios - projectFileName: maui_scenarios_ios.proj - channels: - - main - runtimeFlavor: coreclr - codeGenType: Default - buildConfig: Release - additionalJobIdentifier: CoreCLR - onlySanityCheck: ${{ parameters.onlySanityCheck }} + ${{ if parameters.onlySanityCheck }}: + onlySanityCheck: true From 49843348b2ba0a68bae98a315e009de6adce3fc9 Mon Sep 17 00:00:00 2001 From: Matous Kozak Date: Mon, 7 Sep 2026 16:57:28 +0200 Subject: [PATCH 4/6] Use the selected NuGet config for MAUI feed resolution Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/scenarios/shared/mauisharedpython.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/scenarios/shared/mauisharedpython.py b/src/scenarios/shared/mauisharedpython.py index 4ab318f6551..eb29bba3c35 100644 --- a/src/scenarios/shared/mauisharedpython.py +++ b/src/scenarios/shared/mauisharedpython.py @@ -202,7 +202,7 @@ def _find_latest_manifest_package(packages: list[dict], workload_name: str) -> d return packages[0] -def _resolve_manifest_package(precommands: PreCommands, workload_name: str, feed_url: str) -> tuple[dict, str]: +def _resolve_manifest_package(precommands: PreCommands, workload_name: str, feed_url: str, nuget_config_path: str) -> tuple[dict, str]: '''Resolve the latest manifest package and the feed that supplies its metadata.''' try: packages = precommands.get_packages_for_sdk_from_feed(workload_name, feed_url) @@ -210,7 +210,7 @@ def _resolve_manifest_package(precommands: PreCommands, workload_name: str, feed getLogger().warning(f"Failed to get packages for {workload_name} from latest feed: {e}") getLogger().info("Trying second latest feed as fallback") feed_url = extract_latest_dotnet_feed_from_nuget_config( - path=os.path.join(get_repo_root_path(), "NuGet.config"), + path=nuget_config_path, offset=1 ) getLogger().info(f"Using fallback feed: {feed_url}") @@ -285,7 +285,7 @@ def _normalize_repo_url(url: str) -> str: return f"{parts[-2]}/{parts[-1]}" return url -def _discover_repo_commits(precommands: PreCommands, feed_url: str) -> dict[str, str]: +def _discover_repo_commits(precommands: PreCommands, feed_url: str, nuget_config_path: str) -> dict[str, str]: ''' Discover the commit SHAs for upstream repos by querying the NuGet feed for manifest packages and extracting repository info from their .nuspec metadata. @@ -296,6 +296,7 @@ def _discover_repo_commits(precommands: PreCommands, feed_url: str) -> dict[str, Args: precommands: PreCommands instance for running dotnet commands feed_url: The NuGet feed URL to query + nuget_config_path: Config used to select the initial and fallback feeds Returns: Dict mapping repo names (e.g., "dotnet/android") to commit SHAs. @@ -315,7 +316,7 @@ def _discover_repo_commits(precommands: PreCommands, feed_url: str) -> dict[str, try: getLogger().info(f"Discovering commit SHA for {repo} via {workload_name} package...") - manifest_pkg, manifest_feed = _resolve_manifest_package(precommands, workload_name, feed_url) + manifest_pkg, manifest_feed = _resolve_manifest_package(precommands, workload_name, feed_url, nuget_config_path) # Cache the resolved package so install_latest_maui can reuse it cached_packages[workload_name] = manifest_pkg @@ -450,7 +451,8 @@ def __init__(self, precommands: PreCommands): self.backup_path = self.repo_nuget_config + ".maui_backup" self.downloaded_config_paths: list[str] = [] - def _find_repo_nuget_config(self) -> str: + @staticmethod + def _find_repo_nuget_config() -> str: ''' Find the repo's NuGet.config by walking up from the current directory. This works for both local (c:/Users/.../performance) and pipeline (D:/a/1/s/performance/CorrelationStaging/payload/performance) scenarios. @@ -532,7 +534,7 @@ def __enter__(self): feed_url = extract_latest_dotnet_feed_from_nuget_config( path=self.repo_nuget_config ) - repo_commits = _discover_repo_commits(self.precommands, feed_url) + repo_commits = _discover_repo_commits(self.precommands, feed_url, self.repo_nuget_config) except Exception as e: getLogger().warning(f"Failed to discover repo commits, will use branch HEAD: {e}") @@ -646,9 +648,10 @@ def install_latest_maui( getLogger().info(f"########## Finished installing latest stable {workload_name} workload ##########") return + nuget_config_path = MauiNuGetConfigContext._find_repo_nuget_config() if feed is None: feed = extract_latest_dotnet_feed_from_nuget_config( - path=os.path.join(get_repo_root_path(), "NuGet.config") + path=nuget_config_path ) if workloads is None: @@ -675,7 +678,7 @@ def install_latest_maui( latest_package = cached_packages[workload] getLogger().info(f"Using cached manifest package for {workload}: {latest_package['id']} v{latest_package['latestVersion']}") else: - latest_package, _ = _resolve_manifest_package(precommands, workload, feed) + latest_package, _ = _resolve_manifest_package(precommands, workload, feed, nuget_config_path) getLogger().info(f"Latest package details for {workload}: ID={latest_package['id']}, Version={latest_package['latestVersion']}, SDK_Version={latest_package['sdk_version']}, .NET_Version={latest_package['dotnet_version']}") From 6e5b2e5a4f2a354e7bba6007bc565ad45d24ccc8 Mon Sep 17 00:00:00 2001 From: Matous Kozak Date: Mon, 7 Sep 2026 16:57:28 +0200 Subject: [PATCH 5/6] Repeat two-platform internal workload validation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- azure-pipelines.yml | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4cd188a6a74..4fa0ba4363d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,15 +55,37 @@ schedules: always: true jobs: - - template: /eng/pipelines/sdk-perf-jobs.yml + # Test branch: run one configuration per mobile platform. + - template: /eng/pipelines/templates/build-machine-matrix.yml parameters: - ${{ if or(eq(variables['System.TeamProject'], 'public'), parameters.runPublicJobs) }}: - runPublicJobs: true - ${{ if ne(variables['System.TeamProject'], 'public') }}: - ${{ if or(notin(variables['Build.Reason'], 'PullRequest', 'Manual'), parameters.runPrivateJobs) }}: - runPrivateJobs: true - ${{ if or(eq(variables['Build.CronSchedule.DisplayName'], 'Every 12 hours build'), parameters.runScheduledPrivateJobs) }}: - runScheduledPrivateJobs: true + jobTemplate: /eng/pipelines/templates/run-scenarios-job.yml + buildMachines: + - win-x64-android-arm64-pixel + isPublic: false jobParameters: - ${{ if parameters.onlySanityCheck }}: - onlySanityCheck: true + runKind: maui_scenarios_android + projectFileName: maui_scenarios_android.proj + channels: + - main + runtimeFlavor: coreclr + codeGenType: Default + buildConfig: Release + additionalJobIdentifier: CoreCLR + onlySanityCheck: ${{ parameters.onlySanityCheck }} + + - template: /eng/pipelines/templates/build-machine-matrix.yml + parameters: + jobTemplate: /eng/pipelines/templates/run-scenarios-job.yml + buildMachines: + - osx-x64-ios-arm64 + isPublic: false + jobParameters: + runKind: maui_scenarios_ios + projectFileName: maui_scenarios_ios.proj + channels: + - main + runtimeFlavor: coreclr + codeGenType: Default + buildConfig: Release + additionalJobIdentifier: CoreCLR + onlySanityCheck: ${{ parameters.onlySanityCheck }} From 61ee1d475362f80aabf7d9a0cc3fec5bad941ffc Mon Sep 17 00:00:00 2001 From: Matous Kozak Date: Mon, 7 Sep 2026 16:58:28 +0200 Subject: [PATCH 6/6] Remove temporary pipeline limits after queuing validation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- azure-pipelines.yml | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4fa0ba4363d..4cd188a6a74 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,37 +55,15 @@ schedules: always: true jobs: - # Test branch: run one configuration per mobile platform. - - template: /eng/pipelines/templates/build-machine-matrix.yml + - template: /eng/pipelines/sdk-perf-jobs.yml parameters: - jobTemplate: /eng/pipelines/templates/run-scenarios-job.yml - buildMachines: - - win-x64-android-arm64-pixel - isPublic: false + ${{ if or(eq(variables['System.TeamProject'], 'public'), parameters.runPublicJobs) }}: + runPublicJobs: true + ${{ if ne(variables['System.TeamProject'], 'public') }}: + ${{ if or(notin(variables['Build.Reason'], 'PullRequest', 'Manual'), parameters.runPrivateJobs) }}: + runPrivateJobs: true + ${{ if or(eq(variables['Build.CronSchedule.DisplayName'], 'Every 12 hours build'), parameters.runScheduledPrivateJobs) }}: + runScheduledPrivateJobs: true jobParameters: - runKind: maui_scenarios_android - projectFileName: maui_scenarios_android.proj - channels: - - main - runtimeFlavor: coreclr - codeGenType: Default - buildConfig: Release - additionalJobIdentifier: CoreCLR - onlySanityCheck: ${{ parameters.onlySanityCheck }} - - - template: /eng/pipelines/templates/build-machine-matrix.yml - parameters: - jobTemplate: /eng/pipelines/templates/run-scenarios-job.yml - buildMachines: - - osx-x64-ios-arm64 - isPublic: false - jobParameters: - runKind: maui_scenarios_ios - projectFileName: maui_scenarios_ios.proj - channels: - - main - runtimeFlavor: coreclr - codeGenType: Default - buildConfig: Release - additionalJobIdentifier: CoreCLR - onlySanityCheck: ${{ parameters.onlySanityCheck }} + ${{ if parameters.onlySanityCheck }}: + onlySanityCheck: true