Skip to content

[main] Use Azure Artifacts Maven feed for CFSClean network isolation compliance#35169

Merged
PureWeen merged 2 commits into
mainfrom
cherry-pick/gradle-feed-main
Apr 28, 2026
Merged

[main] Use Azure Artifacts Maven feed for CFSClean network isolation compliance#35169
PureWeen merged 2 commits into
mainfrom
cherry-pick/gradle-feed-main

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

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:44
@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 -- 35169

Or

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

@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 Azure Artifacts feeds so official builds don’t require direct access to Maven Central/Google/Plugin Portal.

Changes:

  • Updates AndroidNative Gradle configuration to use the dotnet-public-maven Azure Artifacts feed (and adds the Azure Artifacts Gradle credential provider plugin).
  • Adds a global Gradle init.gradle and updates pipeline caching steps to install it into GRADLE_USER_HOME after cache restore.
  • Introduces eng/ingest-maven-deps.sh plus 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.

Comment thread src/Core/AndroidNative/settings.gradle Outdated
Comment on lines +1 to +9
// 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:
//

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

Suggested change
// 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:
//

Copilot uses AI. Check for mistakes.
Comment thread eng/ingest-maven-deps.sh
Comment on lines +34 to +38
# Prerequisites:
# - JDK 17+
# - .NET Azure Artifacts credential provider installed
# (https://github.com/microsoft/artifacts-credprovider#installation)
#

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.

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

Copilot uses AI. Check for mistakes.
Comment thread eng/init.gradle Outdated
Comment on lines +1 to +2
// Redirect Maven Central and Google Maven to Azure Artifacts feed
// for CFSClean network isolation compliance.

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

Suggested change
// 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.

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 7299caa into main Apr 28, 2026
40 checks passed
@PureWeen PureWeen deleted the cherry-pick/gradle-feed-main branch April 28, 2026 10:23
@github-actions github-actions Bot added this to the .NET 10 SR7 milestone Apr 28, 2026
PureWeen added a commit that referenced this pull request Apr 29, 2026
<!-- 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
@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