Skip to content

[release/11.0.1xx-preview4] Use Azure Artifacts Maven feed for CFSClean network isolation compliance#35170

Merged
PureWeen merged 2 commits into
release/11.0.1xx-preview4from
cherry-pick/gradle-feed-net11-preview4
Apr 28, 2026
Merged

[release/11.0.1xx-preview4] Use Azure Artifacts Maven feed for CFSClean network isolation compliance#35170
PureWeen merged 2 commits into
release/11.0.1xx-preview4from
cherry-pick/gradle-feed-net11-preview4

Conversation

@PureWeen

Copy link
Copy Markdown
Member

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-sr6 to release/11.0.1xx-preview4.

See #35089 for full details — routes Gradle/Maven dependency resolution through Azure Artifacts feed for CFSClean compliance.

…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>
Copilot AI review requested due to automatic review settings April 27, 2026 18:45
@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35170

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35170"

@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

🔍 Skill Validation Results

✅ Static Checks Passed

Skills checked: 15 | Agents checked: 3

Full validator output
Found 1 skill(s)
[azdo-build-investigator] 📊 azdo-build-investigator: 1,292 BPE tokens [chars/4: 1,209] (detailed ✓), 8 sections, 3 code blocks
[azdo-build-investigator]    ⚠  No numbered workflow steps — agents follow sequenced procedures more reliably.
✅ All checks passed (1 skill(s))
Found 3 agent(s)
Validated 3 agent(s)

✅ All checks passed (3 agent(s))

⏭️ LLM Evaluation: Skipped

No changed skills with eval tests found.

🔍 Full results and investigation steps

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 AndroidNative Gradle configuration to use the dotnet-public-maven Azure Artifacts feed (and adds the Azure Artifacts credprovider plugin).
  • Adds a global Gradle init.gradle and 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.

Comment thread eng/ingest-maven-deps.sh
fi

# Extract failed URLs and curl them with auth
urls=$(echo "$result" | grep "Could not GET\|Could not HEAD" \

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
urls=$(echo "$result" | grep "Could not GET\|Could not HEAD" \
urls=$(echo "$result" | grep -E "Could not (GET|HEAD)" \

Copilot uses AI. Check for mistakes.
// 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:

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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’.

Suggested change
// dependencies, run:
// dependencies, run this from the repository root:

Copilot uses AI. Check for mistakes.
Comment thread eng/ingest-maven-deps.sh
# that used mavenCentral() directly). This does NOT ingest into the feed.
#
# Prerequisites:
# - JDK 17+

Copilot AI Apr 27, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
# - JDK 17+
# - JDK 17+
# - python3

Copilot uses AI. Check for mistakes.
- 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>
@PureWeen PureWeen merged commit 82db1dd into release/11.0.1xx-preview4 Apr 28, 2026
42 of 45 checks passed
@PureWeen PureWeen deleted the cherry-pick/gradle-feed-net11-preview4 branch April 28, 2026 10:24
@github-actions github-actions Bot added this to the .NET 11.0-preview4 milestone Apr 28, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators May 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants