[release/11.0.1xx-preview4] Use Azure Artifacts Maven feed for CFSClean network isolation compliance#35170
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 -- 35170Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35170" |
🔍 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 an Azure Artifacts Maven feed, avoiding direct access to Maven Central/Google repos in network-isolated CI.
Changes:
- Updates
AndroidNativeGradle configuration to use thedotnet-public-mavenAzure Artifacts feed (and adds the Azure Artifacts credprovider plugin). - Adds a global Gradle
init.gradleand installs it in CI after cache restore to rewrite MavenCentral/Google/PluginPortal repos to the feed. - Introduces an ingestion script and updates repo guidance/docs for diagnosing and fixing Gradle 401 “not ingested” failures.
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 standard repositories with Azure Artifacts feeds; documents ingestion requirement. |
| src/Core/AndroidNative/gradle/wrapper/gradle-wrapper.properties | Adds guardrails comment about Gradle 9.x incompatibility with current Android SDK tooling. |
| src/Core/AndroidNative/build.gradle | Routes buildscript repository resolution through dotnet-public-maven. |
| eng/pipelines/common/cache-gradle.yml | Copies eng/init.gradle into $(GRADLE_USER_HOME) after cache restore (per-OS). |
| eng/init.gradle | Global init script to remove MavenCentral/Google/PluginPortal repos and ensure the Azure feed is present. |
| eng/ingest-maven-deps.sh | Adds a local ingestion workflow to pre-populate the Azure feed with new Maven artifacts. |
| .github/skills/azdo-build-investigator/SKILL.md | Documents the Gradle/CFSClean 401 failure signature and remediation. |
| .github/instructions/android.instructions.md | Extends Android guidance to cover Gradle feed/ingestion workflow and relevant file patterns. |
| .github/copilot-instructions.md | Adds always-on guidance for Gradle 401 failures and the ingestion fix path. |
| fi | ||
|
|
||
| # Extract failed URLs and curl them with auth | ||
| urls=$(echo "$result" | grep "Could not GET\|Could not HEAD" \ |
There was a problem hiding this comment.
grep "Could not GET\|Could not HEAD" relies on non-portable alternation syntax (GNU grep extension). On macOS/BSD grep this may not match anything, causing the script to incorrectly think there are no failing URLs. Use grep -E with Could not (GET|HEAD) (or equivalent) so the ingestion loop works cross-platform.
| urls=$(echo "$result" | grep "Could not GET\|Could not HEAD" \ | |
| urls=$(echo "$result" | grep -E "Could not (GET|HEAD)" \ |
| // 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 command reference ./eng/ingest-maven-deps.sh is correct when run from the repo root, but settings.gradle lives under src/Core/AndroidNative. Consider clarifying “from the repository root” to avoid someone running it from this directory and getting a ‘file not found’.
| // dependencies, run: | |
| // dependencies, run this from the repository root: |
| # that used mavenCentral() directly). This does NOT ingest into the feed. | ||
| # | ||
| # Prerequisites: | ||
| # - JDK 17+ |
There was a problem hiding this comment.
The script depends on python3 to parse the credential provider JSON output, but python3 isn’t listed under Prerequisites. Please either add python3 to the prerequisites section or replace the JSON parsing with a tool already listed/guaranteed by the repo to avoid confusing failures on machines without Python.
| # - JDK 17+ | |
| # - JDK 17+ | |
| # - python3 |
- 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>
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-sr6torelease/11.0.1xx-preview4.See #35089 for full details — routes Gradle/Maven dependency resolution through Azure Artifacts feed for CFSClean compliance.