[main] Use Azure Artifacts Maven feed for CFSClean network isolation compliance#35169
Conversation
…nce (#35089) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Problem The official build pipeline (`dotnet-maui`, def 1095) fails because [CFSClean network isolation](#34540) blocks direct access to `repo.maven.apache.org`. This breaks two separate Gradle invocations: 1. **`src/Core/AndroidNative` build** — our own Gradle project 2. **`Microsoft.Android.Sdk.Bindings.Gradle.targets`** — Android SDK binding generator in `Core.csproj` Per [1ES CFS guidance](https://aka.ms/1es/netiso/CFS), the fix is to route all Maven dependency resolution through an Azure Artifacts feed with upstream sources. ## Fix ### Gradle configuration changes - **`settings.gradle`** — Replace `mavenCentral()`, `google()`, `gradlePluginPortal()` with the `dotnet-public-maven` Azure Artifacts feed. Add the [Azure Artifacts Gradle credential provider](https://pkgs.dev.azure.com/artifacts-public/PublicTools/_packaging/AzureArtifacts/maven/v1) plugin (v1.1.1) for local authentication. - **`build.gradle`** — Point `buildscript.repositories` to the same feed for AGP classpath resolution. - **`eng/init.gradle`** — Global Gradle init script that redirects any remaining Maven Central/Google Maven references (e.g. from `Microsoft.Android.Sdk.Bindings.Gradle.targets`) to the feed. Installed into `GRADLE_USER_HOME` by the pipeline. ### Pipeline changes - **`cache-gradle.yml`** — Copy `init.gradle` into `GRADLE_USER_HOME` **after** cache restore to prevent stale cached copies. Uses `$(GRADLE_USER_HOME)` variable for the destination path. ### Why the ingestion script (`eng/ingest-maven-deps.sh`) is needed The `dotnet-public-maven` feed proxies Maven Central, but new packages require an **authenticated first-time pull** to be saved. The Gradle credential provider plugin has two limitations that prevent `dotnet build` from self-ingesting: 1. **Skips entirely in CI** — when `TF_BUILD=True` (Azure Pipelines), the plugin is a no-op 2. **Doesn't cover all Gradle scopes** — the plugin injects auth into `pluginManagement.repositories` and `project.repositories`, but NOT `buildscript.repositories` or AGP's internal `detachedConfiguration` scopes. This means `dotnet build` locally cannot ingest new packages through the Android SDK binding targets even with correct credentials. We verified this by adding an un-ingested package (`io.coil-kt:coil:2.7.0`) — `dotnet build` fails with 401 despite the credential provider authenticating successfully. **Upstream issue:** [microsoft/artifacts-credprovider#671](microsoft/artifacts-credprovider#671) The script works around these gaps by: 1. Acquiring an auth token via the .NET credential provider (MSAL) 2. Pre-ingesting platform-specific artifacts (aapt2) for all OS variants (macOS/Linux/Windows) 3. Running Gradle with `--refresh-dependencies` to bypass local cache 4. Falling back to `curl` with Bearer token for unreachable scopes **Run `./eng/ingest-maven-deps.sh` after adding or updating any Maven/Gradle dependency.** ### Documentation updates - `settings.gradle` — explains the feed setup and when to run the script - `gradle-wrapper.properties` — warning not to upgrade Gradle past 8.x (`dotnet/android#10738`) - `copilot-instructions.md` — always-on guidance for Gradle 401 failures - `azdo-build-investigator/SKILL.md` — error signatures and DO NOTs for CI investigation - `android.instructions.md` — quick reference for Android developers ## Verified - ✅ Internal official build [2961149](https://dev.azure.com/dnceng/internal/_build/results?buildId=2961149) passed — Pack macOS + Pack Windows both green - ✅ Same pattern used by dotnet/aspnetcore ([PR #64962](dotnet/aspnetcore#64962)) - ✅ Feed is public — no auth needed to read already-ingested packages, external contributors can build without credentials - ✅ Locally verified: `dotnet build` works for already-ingested packages, fails for new ones (confirming script is needed) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Matt Mitchell (.NET) <mmitche@microsoft.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35169Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35169" |
🔍 Skill Validation Results✅ Static Checks PassedSkills checked: 15 | Agents checked: 3 Full validator output⏭️ LLM Evaluation: SkippedNo changed skills with eval tests found. |
There was a problem hiding this comment.
Pull request overview
Cherry-picks the CFSClean compliance work to route Android Gradle/Maven dependency resolution through Azure Artifacts feeds so official builds don’t require direct access to Maven Central/Google/Plugin Portal.
Changes:
- Updates
AndroidNativeGradle configuration to use thedotnet-public-mavenAzure Artifacts feed (and adds the Azure Artifacts Gradle credential provider plugin). - Adds a global Gradle
init.gradleand updates pipeline caching steps to install it intoGRADLE_USER_HOMEafter cache restore. - Introduces
eng/ingest-maven-deps.shplus repo guidance updates to support pre-ingesting new Maven artifacts into the feed.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Core/AndroidNative/settings.gradle | Replaces default repositories with Azure Artifacts feeds and documents ingestion requirement. |
| src/Core/AndroidNative/build.gradle | Routes buildscript (AGP) resolution through dotnet-public-maven. |
| src/Core/AndroidNative/gradle/wrapper/gradle-wrapper.properties | Adds guidance to avoid upgrading beyond Gradle 8.x. |
| eng/pipelines/common/cache-gradle.yml | Installs eng/init.gradle into $(GRADLE_USER_HOME) after cache restore. |
| eng/init.gradle | Redirects/removes external Maven repos in Gradle builds to comply with network isolation. |
| eng/ingest-maven-deps.sh | Adds a manual ingestion helper to pre-populate the Azure Artifacts Maven feed. |
| .github/skills/azdo-build-investigator/SKILL.md | Documents CFSClean Gradle 401 failure signatures and remediation. |
| .github/instructions/android.instructions.md | Extends Android instructions scope and adds troubleshooting guidance for feed ingestion. |
| .github/copilot-instructions.md | Adds global repo guidance for Gradle/Maven 401 failures under CFSClean. |
| // All Maven dependencies are resolved through the dnceng Azure Artifacts feed | ||
| // (dotnet-public-maven) for CFSClean network isolation compliance. The feed | ||
| // proxies Maven Central, Google Maven, and Gradle Plugin Portal. | ||
| // | ||
| // IMPORTANT: New packages must be ingested into the feed before CI can use them. | ||
| // The CI credential provider plugin skips auth in Azure Pipelines, so packages | ||
| // that aren't already in the feed will fail with 401. After adding or updating | ||
| // dependencies, run: | ||
| // |
There was a problem hiding this comment.
The header comment says all Maven dependencies resolve through the dotnet-public-maven feed, but pluginManagement.repositories also pulls from the artifacts-public/PublicTools feed for the Azure Artifacts credprovider plugin. Consider updating the wording to reflect that plugins come from a separate Azure Artifacts feed (or narrowing the claim to project dependencies) to avoid confusion when troubleshooting resolution failures.
| // All Maven dependencies are resolved through the dnceng Azure Artifacts feed | |
| // (dotnet-public-maven) for CFSClean network isolation compliance. The feed | |
| // proxies Maven Central, Google Maven, and Gradle Plugin Portal. | |
| // | |
| // IMPORTANT: New packages must be ingested into the feed before CI can use them. | |
| // The CI credential provider plugin skips auth in Azure Pipelines, so packages | |
| // that aren't already in the feed will fail with 401. After adding or updating | |
| // dependencies, run: | |
| // | |
| // Project Maven dependencies are resolved through the dnceng Azure Artifacts | |
| // feed (dotnet-public-maven) for CFSClean network isolation compliance. That | |
| // feed proxies Maven Central, Google Maven, and Gradle Plugin Portal. | |
| // | |
| // Gradle plugin resolution also uses a separate Azure Artifacts feed | |
| // (artifacts-public/PublicTools) for the Azure Artifacts credprovider plugin. | |
| // | |
| // IMPORTANT: New project packages must be ingested into the dotnet-public-maven | |
| // feed before CI can use them. The CI credential provider plugin skips auth in | |
| // Azure Pipelines, so packages that aren't already in the feed will fail with | |
| // 401. After adding or updating dependencies, run: | |
| // |
| # Prerequisites: | ||
| # - JDK 17+ | ||
| # - .NET Azure Artifacts credential provider installed | ||
| # (https://github.com/microsoft/artifacts-credprovider#installation) | ||
| # |
There was a problem hiding this comment.
This script requires python3 to parse the credential provider JSON output, but it's not listed in the prerequisites. Please either add python3 to the prerequisites section (and/or validate it early with a clear error) or switch to a dependency that’s already required/available (e.g., jq if present).
| // Redirect Maven Central and Google Maven to Azure Artifacts feed | ||
| // for CFSClean network isolation compliance. |
There was a problem hiding this comment.
The init script currently says it redirects Maven Central and Google Maven, but the logic also targets plugins.gradle.org (Gradle Plugin Portal). Updating the comment to reflect all the repositories being replaced will make the script’s intent clearer for future maintenance.
| // Redirect Maven Central and Google Maven to Azure Artifacts feed | |
| // for CFSClean network isolation compliance. | |
| // Redirect Maven Central, Google Maven, and the Gradle Plugin Portal | |
| // to Azure Artifacts feed for CFSClean network isolation compliance. |
- init.gradle: mention Gradle Plugin Portal in comment - settings.gradle: clarify credential provider comes from separate feed - ingest-maven-deps.sh: add python3 to prerequisites Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Problem PR #35169 switched all Gradle repos to the Azure Artifacts feed unconditionally. This causes **10+ minute local Android builds** because the credential provider plugin adds significant auth overhead (MSAL token acquisition, feed latency vs direct Maven Central). Reported by team members in India experiencing builds that never complete. ## Fix Use `google()`/`mavenCentral()` for local builds and only switch to the Azure Artifacts feed when `TF_BUILD=True` (Azure Pipelines with CFSClean network isolation). ```groovy if (System.getenv('TF_BUILD') == 'True') { // Azure Artifacts feed for CI } else { // Standard Maven repos for local dev } ``` This also removes the credential provider plugin dependency for local builds since it's not needed when using standard Maven repos. The `init.gradle` still handles the Android SDK bindings Gradle targets redirection in CI via the pipeline template. ## Verified - ✅ Local build uses standard Maven repos (fast, no credential provider overhead) - ✅ CI sets `TF_BUILD=True` which activates the Azure Artifacts feed
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Cherry-pick of #35089 from
release/10.0.1xx-sr6tomain.See #35089 for full details — routes Gradle/Maven dependency resolution through Azure Artifacts feed for CFSClean compliance.