Skip to content

Use Azure Artifacts feed only in CI, standard repos locally#35218

Closed
PureWeen wants to merge 3 commits into
mainfrom
cherry-pick/gradle-feed-main
Closed

Use Azure Artifacts feed only in CI, standard repos locally#35218
PureWeen wants to merge 3 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!

Problem

PR #35169 switched all Gradle repos to the Azure Artifacts feed unconditionally. This causes 10+ minute local builds because the credential provider plugin adds significant auth overhead (MSAL token acquisition, feed latency vs direct Maven Central).

Fix

Use google()/mavenCentral() locally and only switch to the Azure Artifacts feed when TF_BUILD=True (Azure Pipelines with CFSClean). This keeps local builds fast while CI remains compliant.

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)
  • CI sets TF_BUILD=True which activates the Azure Artifacts feed

PureWeen and others added 3 commits April 27, 2026 13:43
…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>
- 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>
The credential provider plugin and Azure Artifacts feed add
significant overhead locally (~10+ min builds). Use google()/
mavenCentral() for local builds and only switch to the Azure
Artifacts feed when TF_BUILD=True (Azure Pipelines).

This also removes the credential provider plugin dependency for
local builds since it's not needed when using standard Maven repos.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 29, 2026 12:05
@github-actions

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

Or

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

@github-actions

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

@PureWeen

Copy link
Copy Markdown
Member Author

Superseded by new PR from latest main

@PureWeen PureWeen closed this Apr 29, 2026

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

This PR restores fast local Gradle builds by using google()/mavenCentral() outside of Azure Pipelines, while keeping CFSClean CI compliant by routing Maven/Gradle resolution through the dotnet-public-maven Azure Artifacts feed (plus adds supporting CI/docs/tooling updates).

Changes:

  • Switch src/Core/AndroidNative Gradle repository configuration to prefer standard public repos locally and Azure Artifacts in CI.
  • Add/install a Gradle init.gradle in CI and introduce a script to pre-ingest Maven dependencies into the Azure Artifacts feed.
  • Update repo guidance/docs to aid diagnosing and fixing Gradle/CFSClean 401 failures.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Core/AndroidNative/settings.gradle Conditional repo selection for plugin/dependency resolution based on CI environment.
src/Core/AndroidNative/build.gradle Conditional buildscript repo selection for CI vs local builds.
src/Core/AndroidNative/gradle/wrapper/gradle-wrapper.properties Adds guardrail comment about staying on Gradle 8.x for current Android SDK compatibility.
eng/pipelines/common/cache-gradle.yml Installs eng/init.gradle after cache restore so CI uses the intended init script.
eng/init.gradle New init script to redirect known public Maven/Plugin Portal repos to Azure Artifacts in CI.
eng/ingest-maven-deps.sh New helper script to pre-ingest dependencies into dotnet-public-maven for CFSClean CI.
.github/skills/azdo-build-investigator/SKILL.md Documents Gradle/CFSClean 401/XAGRDL0000 failure pattern and remediation.
.github/instructions/android.instructions.md Extends Android guidance scope to AndroidNative/Gradle tooling and adds CFSClean dependency ingestion notes.
.github/copilot-instructions.md Adds CFSClean Gradle/Maven dependency failure guidance and the ingestion script pointer.

@@ -1,8 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
def isCI = System.getenv('TF_BUILD') == 'True' || System.getenv('CI') != null
Comment thread eng/ingest-maven-deps.sh
Comment on lines +88 to +100
cd "$ANDROID_DIR"
if ! ./gradlew build --no-daemon --refresh-dependencies \
-Dazure.artifacts.credprovider.nonInteractive=true \
-Dazure.artifacts.credprovider.isRetry=true 2>&1 | tail -20; then
echo "WARNING: Initial Gradle build failed (expected if packages need ingestion). Continuing..."
fi

# Step 4: Loop — build, find missing packages, curl-ingest them
echo ""
echo "Step 3/3: Ingesting any remaining packages via REST API..."
for i in $(seq 1 30); do
result=$(./gradlew build --no-daemon \
-Dazure.artifacts.credprovider.nonInteractive=true 2>&1 || true)
condition: ne(variables['Agent.OS'], 'Windows_NT')

- pwsh: |
Copy-Item "${{ parameters.checkoutDirectory }}/eng/init.gradle" (Join-Path "$(GRADLE_USER_HOME)" "init.gradle")
Comment on lines +11 to +13
if (System.getenv('TF_BUILD') == 'True') {
maven {
url = 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-maven/maven/v1'
@github-actions github-actions Bot locked and limited conversation to collaborators May 30, 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