From fe1aaf968bb5cbca21f0078753b66d60e1440f9f Mon Sep 17 00:00:00 2001 From: AzureFunctionsJava Date: Wed, 5 Aug 2026 13:51:38 -0500 Subject: [PATCH 1/3] Onboard Maven restores to CFS --- .gitattributes | 7 ++ .gitignore | 3 + README.md | 18 +++ dockertests/app-src/BlobSdkType/pom.xml | 18 +++ dockertests/app-src/TimezoneCheck/pom.xml | 18 +++ emulatedtests/pom.xml | 14 +-- endtoendtests/pom.xml | 14 +-- eng/ci/templates/jobs/build.yml | 11 ++ .../templates/jobs/run-docker-tests-linux.yml | 11 ++ .../jobs/run-emulated-tests-linux.yml | 11 ++ .../jobs/run-emulated-tests-windows.yml | 11 ++ .../official/jobs/build-artifacts.yml | 11 ++ .../official/jobs/run-e2e-tests-linux.yml | 11 ++ .../official/jobs/run-e2e-tests-windows.yml | 11 ++ .../Install-MavenCredentialProvider.ps1 | 113 ++++++++++++++++++ eng/scripts/install-maven-credprovider.sh | 88 ++++++++++++++ pom.xml | 19 ++- .../dagger-function/pom.xml | 18 +++ .../guice-function/pom.xml | 18 +++ samples/spring-cloud-example/pom.xml | 18 +++ settings.xml | 25 ++++ warmup-function/pom.xml | 18 +++ 22 files changed, 466 insertions(+), 20 deletions(-) create mode 100644 .gitattributes create mode 100644 eng/scripts/Install-MavenCredentialProvider.ps1 create mode 100644 eng/scripts/install-maven-credprovider.sh create mode 100644 settings.xml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..e078bd06 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# Shell scripts must retain LF endings on every platform. +*.sh text eol=lf + +# Preserve the existing CRLF convention in these standalone sample projects. +warmup-function/pom.xml whitespace=cr-at-eol +samples/dependency-injection-example/dagger-function/pom.xml whitespace=cr-at-eol +samples/dependency-injection-example/guice-function/pom.xml whitespace=cr-at-eol \ No newline at end of file diff --git a/.gitignore b/.gitignore index b9fec809..449e37cf 100644 --- a/.gitignore +++ b/.gitignore @@ -259,6 +259,9 @@ target/ .project *.iml dependency-reduced-pom.xml +# The Maven credential provider is an opt-in for ingesting uncached packages. CI authenticates +# with MavenAuthenticate@0, and committing the extension would break anonymous restores. +.mvn/ /functions-coffee-order-demo/src/main/functions/functions-coffee-order.jar /functions-coffee-order-demo/src/main/arduino/.build/ pkg/ diff --git a/README.md b/README.md index e4c19ed6..00bc523c 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,24 @@ Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. * Run all maven commands under the root folder of this repository +Maven packages and plugins are restored through the `upstream-public` Azure Artifacts feed. Every +tracked `pom.xml` overrides Maven's `central` repository, and `settings.xml` mirrors early plugin and +extension requests to the same feed. + +Packages already cached in the feed can be restored anonymously. When a new package version has not +been cached yet, a Microsoft developer can install the Azure Artifacts credential provider: + +```powershell +./eng/scripts/Install-MavenCredentialProvider.ps1 +``` + +```bash +./eng/scripts/install-maven-credprovider.sh +``` + +The helper creates a local `.mvn/extensions.xml`, which is intentionally ignored by Git. CI installs +the repository `settings.xml` and authenticates with `MavenAuthenticate@0` before running Maven. + ## IntelliJ * Import the root folder of this repository as an existing project in IntelliJ diff --git a/dockertests/app-src/BlobSdkType/pom.xml b/dockertests/app-src/BlobSdkType/pom.xml index 0468e205..004af88a 100644 --- a/dockertests/app-src/BlobSdkType/pom.xml +++ b/dockertests/app-src/BlobSdkType/pom.xml @@ -24,6 +24,24 @@ functions-quickstart-java-azd-eventgrid-blob + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + com.microsoft.azure.functions diff --git a/dockertests/app-src/TimezoneCheck/pom.xml b/dockertests/app-src/TimezoneCheck/pom.xml index 9abd2db0..4ea45b0b 100644 --- a/dockertests/app-src/TimezoneCheck/pom.xml +++ b/dockertests/app-src/TimezoneCheck/pom.xml @@ -22,6 +22,24 @@ timezone-check + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + com.microsoft.azure.functions diff --git a/emulatedtests/pom.xml b/emulatedtests/pom.xml index 05b0a760..d65698f5 100644 --- a/emulatedtests/pom.xml +++ b/emulatedtests/pom.xml @@ -35,11 +35,10 @@ - maven.snapshots - Maven Central Snapshot Repository - https://oss.sonatype.org/content/repositories/snapshots/ + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 - false + true true @@ -49,11 +48,10 @@ - maven.snapshots - Maven Central Snapshot Repository - https://oss.sonatype.org/content/repositories/snapshots/ + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 - false + true true diff --git a/endtoendtests/pom.xml b/endtoendtests/pom.xml index 22fce2cf..49a09a27 100644 --- a/endtoendtests/pom.xml +++ b/endtoendtests/pom.xml @@ -33,11 +33,10 @@ - maven.snapshots - Maven Central Snapshot Repository - https://oss.sonatype.org/content/repositories/snapshots/ + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 - false + true true @@ -47,11 +46,10 @@ - maven.snapshots - Maven Central Snapshot Repository - https://oss.sonatype.org/content/repositories/snapshots/ + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 - false + true true diff --git a/eng/ci/templates/jobs/build.yml b/eng/ci/templates/jobs/build.yml index 9d229516..bf11fd27 100644 --- a/eng/ci/templates/jobs/build.yml +++ b/eng/ci/templates/jobs/build.yml @@ -8,6 +8,17 @@ jobs: os: windows steps: + # Maven resolves plugins and extensions before a pom's repositories are honored. Install the + # mirror before MavenAuthenticate@0, which adds credentials to the same settings file. + - pwsh: | + $m2 = Join-Path $HOME '.m2' + New-Item -ItemType Directory -Path $m2 -Force | Out-Null + Copy-Item '$(Build.SourcesDirectory)/settings.xml' (Join-Path $m2 'settings.xml') -Force + displayName: 'Install Maven settings.xml' + - task: MavenAuthenticate@0 + displayName: 'Authenticate Maven to CFS' + inputs: + artifactsFeeds: upstream-public - pwsh: | Get-Command mvn displayName: 'Check Maven is installed' diff --git a/eng/ci/templates/jobs/run-docker-tests-linux.yml b/eng/ci/templates/jobs/run-docker-tests-linux.yml index e4ac90e0..44a5bd8e 100644 --- a/eng/ci/templates/jobs/run-docker-tests-linux.yml +++ b/eng/ci/templates/jobs/run-docker-tests-linux.yml @@ -25,6 +25,17 @@ jobs: javaVersion: '21' steps: + # Maven resolves plugins and extensions before a pom's repositories are honored. Install the + # mirror before MavenAuthenticate@0, which adds credentials to the same settings file. + - pwsh: | + $m2 = Join-Path $HOME '.m2' + New-Item -ItemType Directory -Path $m2 -Force | Out-Null + Copy-Item '$(Build.SourcesDirectory)/settings.xml' (Join-Path $m2 'settings.xml') -Force + displayName: 'Install Maven settings.xml' + - task: MavenAuthenticate@0 + displayName: 'Authenticate Maven to CFS' + inputs: + artifactsFeeds: upstream-public - bash: | echo "=== disk BEFORE cleanup ===" df -h / diff --git a/eng/ci/templates/jobs/run-emulated-tests-linux.yml b/eng/ci/templates/jobs/run-emulated-tests-linux.yml index 63ab4346..f584d95e 100644 --- a/eng/ci/templates/jobs/run-emulated-tests-linux.yml +++ b/eng/ci/templates/jobs/run-emulated-tests-linux.yml @@ -45,6 +45,17 @@ jobs: JAVA_VERSION_SPEC: '25' steps: + # Maven resolves plugins and extensions before a pom's repositories are honored. Install the + # mirror before MavenAuthenticate@0, which adds credentials to the same settings file. + - pwsh: | + $m2 = Join-Path $HOME '.m2' + New-Item -ItemType Directory -Path $m2 -Force | Out-Null + Copy-Item '$(Build.SourcesDirectory)/settings.xml' (Join-Path $m2 'settings.xml') -Force + displayName: 'Install Maven settings.xml' + - task: MavenAuthenticate@0 + displayName: 'Authenticate Maven to CFS' + inputs: + artifactsFeeds: upstream-public - bash: | echo "=== disk BEFORE cleanup ===" df -h / diff --git a/eng/ci/templates/jobs/run-emulated-tests-windows.yml b/eng/ci/templates/jobs/run-emulated-tests-windows.yml index b2444567..b0fc4ca5 100644 --- a/eng/ci/templates/jobs/run-emulated-tests-windows.yml +++ b/eng/ci/templates/jobs/run-emulated-tests-windows.yml @@ -45,6 +45,17 @@ jobs: JAVA_VERSION_SPEC: '25' steps: + # Maven resolves plugins and extensions before a pom's repositories are honored. Install the + # mirror before MavenAuthenticate@0, which adds credentials to the same settings file. + - pwsh: | + $m2 = Join-Path $HOME '.m2' + New-Item -ItemType Directory -Path $m2 -Force | Out-Null + Copy-Item '$(Build.SourcesDirectory)/settings.xml' (Join-Path $m2 'settings.xml') -Force + displayName: 'Install Maven settings.xml' + - task: MavenAuthenticate@0 + displayName: 'Authenticate Maven to CFS' + inputs: + artifactsFeeds: upstream-public - task: NuGetToolInstaller@1 inputs: checkLatest: true diff --git a/eng/ci/templates/official/jobs/build-artifacts.yml b/eng/ci/templates/official/jobs/build-artifacts.yml index d9c4a6b5..02509bf8 100644 --- a/eng/ci/templates/official/jobs/build-artifacts.yml +++ b/eng/ci/templates/official/jobs/build-artifacts.yml @@ -28,6 +28,17 @@ jobs: isRelease: $[variables.isReleaseTemp] steps: + # Maven resolves plugins and extensions before a pom's repositories are honored. Install the + # mirror before MavenAuthenticate@0, which adds credentials to the same settings file. + - pwsh: | + $m2 = Join-Path $HOME '.m2' + New-Item -ItemType Directory -Path $m2 -Force | Out-Null + Copy-Item '$(Build.SourcesDirectory)/settings.xml' (Join-Path $m2 'settings.xml') -Force + displayName: 'Install Maven settings.xml' + - task: MavenAuthenticate@0 + displayName: 'Authenticate Maven to CFS' + inputs: + artifactsFeeds: upstream-public - task: NuGetToolInstaller@1 inputs: checkLatest: true diff --git a/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml b/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml index 6584dcec..6d0df968 100644 --- a/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml +++ b/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml @@ -38,6 +38,17 @@ jobs: JAVA_VERSION_SPEC: '21' steps: + # Maven resolves plugins and extensions before a pom's repositories are honored. Install the + # mirror before MavenAuthenticate@0, which adds credentials to the same settings file. + - pwsh: | + $m2 = Join-Path $HOME '.m2' + New-Item -ItemType Directory -Path $m2 -Force | Out-Null + Copy-Item '$(Build.SourcesDirectory)/settings.xml' (Join-Path $m2 'settings.xml') -Force + displayName: 'Install Maven settings.xml' + - task: MavenAuthenticate@0 + displayName: 'Authenticate Maven to CFS' + inputs: + artifactsFeeds: upstream-public - task: NuGetToolInstaller@1 inputs: checkLatest: true diff --git a/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml b/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml index 3862cec4..3a6a31b4 100644 --- a/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml +++ b/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml @@ -34,6 +34,17 @@ jobs: JDK_PATH: 'jdk-21.0.1+12' steps: + # Maven resolves plugins and extensions before a pom's repositories are honored. Install the + # mirror before MavenAuthenticate@0, which adds credentials to the same settings file. + - pwsh: | + $m2 = Join-Path $HOME '.m2' + New-Item -ItemType Directory -Path $m2 -Force | Out-Null + Copy-Item '$(Build.SourcesDirectory)/settings.xml' (Join-Path $m2 'settings.xml') -Force + displayName: 'Install Maven settings.xml' + - task: MavenAuthenticate@0 + displayName: 'Authenticate Maven to CFS' + inputs: + artifactsFeeds: upstream-public - task: NuGetToolInstaller@1 inputs: checkLatest: true diff --git a/eng/scripts/Install-MavenCredentialProvider.ps1 b/eng/scripts/Install-MavenCredentialProvider.ps1 new file mode 100644 index 00000000..ca71763a --- /dev/null +++ b/eng/scripts/Install-MavenCredentialProvider.ps1 @@ -0,0 +1,113 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Installs the Azure Artifacts Maven credential provider for local development. + +.DESCRIPTION + Anonymous restores work for packages already cached in the CFS feed. Microsoft developers can + run this script to authenticate and ingest a package version that has not been cached yet. + +.PARAMETER Version + Credential provider version to install. + +.PARAMETER LocalRepositoryPath + Maven local repository path. Defaults to ~/.m2/repository. + +.PARAMETER Force + Reinstalls the provider and overwrites the generated .mvn/extensions.xml. +#> + +[CmdletBinding()] +param( + [string] $Version = '3.2.1', + [string] $LocalRepositoryPath, + [switch] $Force +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$groupId = 'com.microsoft.azure' +$artifactId = 'artifacts-maven-credprovider' +$bootstrapFeed = 'https://pkgs.dev.azure.com/artifacts-public/PublicTools/_packaging/AzureArtifacts/maven/v1' +$repositoryId = 'central' +$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..' '..')).Path + +if (-not (Get-Command mvn -ErrorAction SilentlyContinue)) { + throw "Maven ('mvn') was not found on PATH." +} + +$customLocalRepository = -not [string]::IsNullOrWhiteSpace($LocalRepositoryPath) +if (-not $customLocalRepository) { + $LocalRepositoryPath = Join-Path $HOME '.m2' 'repository' +} + +$artifactDirectory = $LocalRepositoryPath +foreach ($segment in ($groupId.Split('.') + @($artifactId, $Version))) { + $artifactDirectory = Join-Path $artifactDirectory $segment +} +$artifactPath = Join-Path $artifactDirectory "$artifactId-$Version.jar" + +if ($Force -or -not (Test-Path $artifactPath)) { + $workingDirectory = Join-Path ([IO.Path]::GetTempPath()) ('maven-credprovider-' + [Guid]::NewGuid().ToString('n')) + New-Item -ItemType Directory -Path $workingDirectory -Force | Out-Null + try { + Push-Location $workingDirectory + try { + $arguments = @( + '--batch-mode' + 'dependency:get' + "-Dartifact=${groupId}:${artifactId}:${Version}" + "-DremoteRepositories=${repositoryId}::::${bootstrapFeed}" + ) + if ($customLocalRepository) { + $arguments += "-Dmaven.repo.local=$LocalRepositoryPath" + } + & mvn @arguments + if ($LASTEXITCODE -ne 0) { + throw "Maven failed with exit code $LASTEXITCODE." + } + } + finally { + Pop-Location + } + } + finally { + Remove-Item $workingDirectory -Recurse -Force -ErrorAction SilentlyContinue + } +} + +if (-not (Test-Path $artifactPath)) { + throw "Credential provider was not found at '$artifactPath' after installation." +} + +$extensionsDirectory = Join-Path $repoRoot '.mvn' +$extensionsPath = Join-Path $extensionsDirectory 'extensions.xml' +if ((Test-Path $extensionsPath) -and -not $Force) { + $existing = Get-Content $extensionsPath -Raw + if ($existing -notmatch [regex]::Escape($artifactId)) { + throw "'$extensionsPath' contains an unmanaged Maven extension. Use -Force to overwrite it." + } + if ($existing -match "\s*$([regex]::Escape($Version))\s*") { + Write-Host "Maven credential provider $Version is already configured." + return + } +} + +$extensions = @" + + + + $groupId + $artifactId + $Version + + +"@ + +New-Item -ItemType Directory -Path $extensionsDirectory -Force | Out-Null +Set-Content -Path $extensionsPath -Value $extensions -Encoding utf8 +Write-Host "Configured Maven credential provider $Version in '$extensionsPath'." \ No newline at end of file diff --git a/eng/scripts/install-maven-credprovider.sh b/eng/scripts/install-maven-credprovider.sh new file mode 100644 index 00000000..626b77e9 --- /dev/null +++ b/eng/scripts/install-maven-credprovider.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +set -euo pipefail + +group_id='com.microsoft.azure' +artifact_id='artifacts-maven-credprovider' +bootstrap_feed='https://pkgs.dev.azure.com/artifacts-public/PublicTools/_packaging/AzureArtifacts/maven/v1' +repository_id='central' +version='3.2.1' +local_repository_path='' +force=false + +usage() { + cat <<'EOF' +Usage: install-maven-credprovider.sh [options] + +Options: + -v, --version Credential provider version to install. + -l, --local-repository Maven local repository path. + -f, --force Reinstall and overwrite .mvn/extensions.xml. + -h, --help Show this help text. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -v|--version) [[ $# -ge 2 ]] || { echo "error: $1 requires a value" >&2; exit 1; }; version="$2"; shift 2 ;; + -l|--local-repository) [[ $# -ge 2 ]] || { echo "error: $1 requires a value" >&2; exit 1; }; local_repository_path="$2"; shift 2 ;; + -f|--force) force=true; shift ;; + -h|--help) usage; exit 0 ;; + *) echo "error: unknown argument '$1'" >&2; usage >&2; exit 1 ;; + esac +done + +command -v mvn >/dev/null 2>&1 || { echo "error: Maven ('mvn') was not found on PATH." >&2; exit 1; } + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd -- "$script_dir/../.." && pwd)" +custom_local_repository=true +if [[ -z "$local_repository_path" ]]; then + custom_local_repository=false + local_repository_path="$HOME/.m2/repository" +fi + +group_path="${group_id//./\/}" +artifact_path="$local_repository_path/$group_path/$artifact_id/$version/$artifact_id-$version.jar" +if [[ "$force" == true || ! -f "$artifact_path" ]]; then + working_directory="$(mktemp -d)" + trap 'rm -rf "$working_directory"' EXIT + arguments=( + --batch-mode + dependency:get + "-Dartifact=${group_id}:${artifact_id}:${version}" + "-DremoteRepositories=${repository_id}::::${bootstrap_feed}" + ) + if [[ "$custom_local_repository" == true ]]; then + arguments+=("-Dmaven.repo.local=$local_repository_path") + fi + (cd "$working_directory" && mvn "${arguments[@]}") +fi + +[[ -f "$artifact_path" ]] || { echo "error: credential provider was not found at '$artifact_path'." >&2; exit 1; } + +extensions_directory="$repo_root/.mvn" +extensions_path="$extensions_directory/extensions.xml" +if [[ -f "$extensions_path" && "$force" != true ]]; then + grep -q "$artifact_id" "$extensions_path" || { echo "error: '$extensions_path' contains an unmanaged Maven extension." >&2; exit 1; } + if grep -qE "[[:space:]]*${version//./\.}[[:space:]]*" "$extensions_path"; then + echo "Maven credential provider $version is already configured." + exit 0 + fi +fi + +mkdir -p "$extensions_directory" +cat >"$extensions_path" < + + + $group_id + $artifact_id + $version + + +EOF + +echo "Configured Maven credential provider $version in '$extensions_path'." \ No newline at end of file diff --git a/pom.xml b/pom.xml index f2713957..c5413885 100644 --- a/pom.xml +++ b/pom.xml @@ -45,17 +45,28 @@ - maven.snapshots - Maven Central Snapshot Repository - https://oss.sonatype.org/content/repositories/snapshots/ + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 - false + true true + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + + true + + + true + + + com.microsoft.azure.functions diff --git a/samples/dependency-injection-example/dagger-function/pom.xml b/samples/dependency-injection-example/dagger-function/pom.xml index 0aa4f14e..426e0171 100644 --- a/samples/dependency-injection-example/dagger-function/pom.xml +++ b/samples/dependency-injection-example/dagger-function/pom.xml @@ -17,6 +17,24 @@ dagger-function-20221101192648097 + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + com.microsoft.azure.functions diff --git a/samples/dependency-injection-example/guice-function/pom.xml b/samples/dependency-injection-example/guice-function/pom.xml index de01f078..43d638d6 100644 --- a/samples/dependency-injection-example/guice-function/pom.xml +++ b/samples/dependency-injection-example/guice-function/pom.xml @@ -17,6 +17,24 @@ juice-test-20220912191553642 + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + com.microsoft.azure.functions diff --git a/samples/spring-cloud-example/pom.xml b/samples/spring-cloud-example/pom.xml index 7e56a510..c84907a8 100644 --- a/samples/spring-cloud-example/pom.xml +++ b/samples/spring-cloud-example/pom.xml @@ -31,6 +31,24 @@ sample-service-plan EP1 + + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + diff --git a/settings.xml b/settings.xml new file mode 100644 index 00000000..dd6fa56a --- /dev/null +++ b/settings.xml @@ -0,0 +1,25 @@ + + + + + + upstream-public + Azure Functions public upstream feed + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + central + + + \ No newline at end of file diff --git a/warmup-function/pom.xml b/warmup-function/pom.xml index 50e73ac9..2110242d 100644 --- a/warmup-function/pom.xml +++ b/warmup-function/pom.xml @@ -14,6 +14,24 @@ 1.8 + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + + + + central + https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1 + true + true + + + com.microsoft.azure.functions From 3bea732a3f9848699473811b737b941e25b95f5c Mon Sep 17 00:00:00 2001 From: AzureFunctionsJava Date: Wed, 5 Aug 2026 21:41:58 -0500 Subject: [PATCH 2/3] Align CFS tooling with reference --- .gitattributes | 2 +- README.md | 102 ++++++++++- dockertests/app-src/BlobSdkType/pom.xml | 2 +- eng/ci/templates/jobs/build.yml | 2 +- .../jobs/run-emulated-tests-linux.yml | 2 +- .../jobs/run-emulated-tests-windows.yml | 2 +- .../official/jobs/build-artifacts.yml | 2 +- .../official/jobs/run-e2e-tests-linux.yml | 2 +- .../official/jobs/run-e2e-tests-windows.yml | 2 +- .../Install-MavenCredentialProvider.ps1 | 97 +++++++--- eng/scripts/install-maven-credprovider.sh | 171 +++++++++++++----- pom.xml | 2 +- settings.xml | 2 +- 13 files changed, 297 insertions(+), 93 deletions(-) diff --git a/.gitattributes b/.gitattributes index e078bd06..28fc1d67 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,4 +4,4 @@ # Preserve the existing CRLF convention in these standalone sample projects. warmup-function/pom.xml whitespace=cr-at-eol samples/dependency-injection-example/dagger-function/pom.xml whitespace=cr-at-eol -samples/dependency-injection-example/guice-function/pom.xml whitespace=cr-at-eol \ No newline at end of file +samples/dependency-injection-example/guice-function/pom.xml whitespace=cr-at-eol diff --git a/README.md b/README.md index 00bc523c..f6724e09 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,49 @@ Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for more information. * Run all maven commands under the root folder of this repository -Maven packages and plugins are restored through the `upstream-public` Azure Artifacts feed. Every -tracked `pom.xml` overrides Maven's `central` repository, and `settings.xml` mirrors early plugin and -extension requests to the same feed. +### Package feed -Packages already cached in the feed can be restored anonymously. When a new package version has not -been cached yet, a Microsoft developer can install the Azure Artifacts credential provider: +All Maven packages and plugins are restored from the `upstream-public` Azure Artifacts feed +(`https://pkgs.dev.azure.com/azfunc/public/_packaging/upstream-public/maven/v1`), which is configured +as the `central` repository in every `pom.xml` in this repository. + +The repository root also has a [`settings.xml`](settings.xml) that mirrors `central` to the same +feed. It exists because a `pom.xml` cannot cover everything: + +- Maven resolves build extensions and plugin prefixes *before* a pom's `` are honored, + so those requests would otherwise go straight to Maven Central. +- `MavenAuthenticate@0` and the credential provider key credentials off the Azure Artifacts *feed + name* (`upstream-public`), while the pom repository id must be `central` in order to override the + id Maven inherits from the Super POM. The mirror id bridges the two. + +CI installs this file to `~/.m2/settings.xml`. Locally you only need it when pulling a package or +version the feed has not cached yet, in which case pass it explicitly with `mvn -s settings.xml`. + +#### Anonymous restore (default) + +The feed allows anonymous reads, so no credentials are required to build once a package version has +been saved to the feed. External contributors and fresh clones need no setup. `mvn` just works. +Never commit credentials or a `` entry to `settings.xml` in this repository because doing so +would force authentication on everyone. + +#### Authenticating (Microsoft developers only) + +Authentication is only needed to *ingest* a package version that the feed has not cached yet. The +first restore of any new or upgraded dependency will fail anonymously with: + +> No local versions of package '...'; please provide authentication to access versions from upstream +> that have not yet been saved to your feed. + +When that happens, a Microsoft developer with access to the `azfunc/public` project must run the +restore once with credentials, which pulls the version from upstream and saves it to the feed. Every +subsequent anonymous restore then succeeds. + +The recommended way to authenticate is the `artifacts-maven-credprovider`, which acquires a token via +Entra ID so you do not have to manage a PAT. + +Run the helper script for your shell from the root of your clone. It installs the credential provider +into your local Maven repository if it is missing, then writes `.mvn/extensions.xml`. Both scripts +are idempotent, so re-running them is safe: ```powershell ./eng/scripts/Install-MavenCredentialProvider.ps1 @@ -30,8 +67,59 @@ been cached yet, a Microsoft developer can install the Azure Artifacts credentia ./eng/scripts/install-maven-credprovider.sh ``` -The helper creates a local `.mvn/extensions.xml`, which is intentionally ignored by Git. CI installs -the repository `settings.xml` and authenticates with `MavenAuthenticate@0` before running Maven. +Pass `-Version` / `--version` to install a different release, and `-Force` / `--force` to reinstall or +to overwrite an `.mvn/extensions.xml` the script does not manage. + +If you would rather do it by hand, the equivalent steps are: + +1. Bootstrap the credential provider once per machine. Run this from a directory outside any Maven + project, such as your home directory. It downloads the extension from the public `AzureArtifacts` + tools feed, which needs no authentication: + + ```powershell + mvn dependency:get "-Dartifact=com.microsoft.azure:artifacts-maven-credprovider:3.2.1" "-DremoteRepositories=central::::https://pkgs.dev.azure.com/artifacts-public/PublicTools/_packaging/AzureArtifacts/maven/v1" + ``` + + Using the repository id `central` matters. Maven records the extension as having come from + `central`, which is the same id this repository's `pom.xml` files declare, so the cached copy + validates during later builds. + +2. Create `.mvn/extensions.xml` at the root of your clone: + + ```xml + + + com.microsoft.azure + artifacts-maven-credprovider + 3.2.1 + + + ``` + +`.mvn/` is deliberately listed in `.gitignore`. Do not commit it. The extension exits when it +detects a build context, and committing it would break anonymous restores for everyone else. + +If you would rather not use the credential provider, you can instead add a `` entry to your +user-level `~/.m2/settings.xml` (never to a file inside this repository), using an Azure DevOps +personal access token with Packaging read and write scope: + +```xml + + + + + central + azfunc + [PERSONAL_ACCESS_TOKEN] + + + +``` + +CI covers this automatically. The `MavenAuthenticate@0` task in the build templates authenticates the +`central` repository, so merged changes to dependency versions are ingested by the pipeline. The +credential provider is not used in pipelines. ## IntelliJ diff --git a/dockertests/app-src/BlobSdkType/pom.xml b/dockertests/app-src/BlobSdkType/pom.xml index 004af88a..b86b0cd7 100644 --- a/dockertests/app-src/BlobSdkType/pom.xml +++ b/dockertests/app-src/BlobSdkType/pom.xml @@ -117,4 +117,4 @@ - \ No newline at end of file + diff --git a/eng/ci/templates/jobs/build.yml b/eng/ci/templates/jobs/build.yml index bf11fd27..c2ae0ad3 100644 --- a/eng/ci/templates/jobs/build.yml +++ b/eng/ci/templates/jobs/build.yml @@ -27,4 +27,4 @@ jobs: displayName: 'Check default java version' - pwsh: | mvn clean package - displayName: 'Build java worker' \ No newline at end of file + displayName: 'Build java worker' diff --git a/eng/ci/templates/jobs/run-emulated-tests-linux.yml b/eng/ci/templates/jobs/run-emulated-tests-linux.yml index f584d95e..3dcd0fa0 100644 --- a/eng/ci/templates/jobs/run-emulated-tests-linux.yml +++ b/eng/ci/templates/jobs/run-emulated-tests-linux.yml @@ -159,4 +159,4 @@ jobs: env: AzureWebJobsStorage: "UseDevelopmentStorage=true" displayName: 'Build & Run tests' - continueOnError: false \ No newline at end of file + continueOnError: false diff --git a/eng/ci/templates/jobs/run-emulated-tests-windows.yml b/eng/ci/templates/jobs/run-emulated-tests-windows.yml index b0fc4ca5..66d6e222 100644 --- a/eng/ci/templates/jobs/run-emulated-tests-windows.yml +++ b/eng/ci/templates/jobs/run-emulated-tests-windows.yml @@ -139,4 +139,4 @@ jobs: env: AzureWebJobsStorage: "UseDevelopmentStorage=true" displayName: 'Build & Run tests' - continueOnError: false \ No newline at end of file + continueOnError: false diff --git a/eng/ci/templates/official/jobs/build-artifacts.yml b/eng/ci/templates/official/jobs/build-artifacts.yml index 02509bf8..9afef0c2 100644 --- a/eng/ci/templates/official/jobs/build-artifacts.yml +++ b/eng/ci/templates/official/jobs/build-artifacts.yml @@ -68,4 +68,4 @@ jobs: Contents: '*.nupkg' TargetFolder: '$(Build.ArtifactStagingDirectory)' CleanTargetFolder: true - displayName: 'Copying files for artifacts' \ No newline at end of file + displayName: 'Copying files for artifacts' diff --git a/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml b/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml index 6d0df968..6e879df6 100644 --- a/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml +++ b/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml @@ -148,4 +148,4 @@ jobs: ApplicationInsightAPPID: $(ApplicationInsightAPPID) ApplicationInsightAgentVersion: $(ApplicationInsightAgentVersion) displayName: 'Build & Run tests' - continueOnError: false \ No newline at end of file + continueOnError: false diff --git a/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml b/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml index 3a6a31b4..03bd1f51 100644 --- a/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml +++ b/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml @@ -133,4 +133,4 @@ jobs: ApplicationInsightAPPID: $(ApplicationInsightAPPID) ApplicationInsightAgentVersion: $(ApplicationInsightAgentVersion) displayName: 'Build & Run tests' - continueOnError: false \ No newline at end of file + continueOnError: false diff --git a/eng/scripts/Install-MavenCredentialProvider.ps1 b/eng/scripts/Install-MavenCredentialProvider.ps1 index ca71763a..254508f9 100644 --- a/eng/scripts/Install-MavenCredentialProvider.ps1 +++ b/eng/scripts/Install-MavenCredentialProvider.ps1 @@ -2,20 +2,37 @@ <# .SYNOPSIS - Installs the Azure Artifacts Maven credential provider for local development. + Bootstraps the Azure Artifacts Maven credential provider for local development. .DESCRIPTION - Anonymous restores work for packages already cached in the CFS feed. Microsoft developers can - run this script to authenticate and ingest a package version that has not been cached yet. + Maven packages for this repository are restored from an Azure Artifacts feed. Reads are + anonymous, so this script is only needed by Microsoft developers who have to ingest a package + version that the feed has not cached yet. + + The script: + 1. Verifies the credential provider is present in the local Maven repository, and downloads it + from the public AzureArtifacts tools feed if it is not. + 2. Writes '.mvn/extensions.xml' at the root of the repository so Maven loads the provider. + + '.mvn/' is intentionally listed in .gitignore. The extension exits when it detects a build + context, and committing it would force an authenticated restore on anonymous consumers. Azure + Pipelines uses the MavenAuthenticate@0 task instead. .PARAMETER Version - Credential provider version to install. + Version of the credential provider to install. Defaults to the version pinned by this script. .PARAMETER LocalRepositoryPath - Maven local repository path. Defaults to ~/.m2/repository. + Path to the local Maven repository. Defaults to '~/.m2/repository'. .PARAMETER Force - Reinstalls the provider and overwrites the generated .mvn/extensions.xml. + Overwrite an existing '.mvn/extensions.xml' even if it declares extensions this script does not + manage, and re-download the credential provider even when it is already installed. + +.EXAMPLE + ./eng/scripts/Install-MavenCredentialProvider.ps1 + +.LINK + https://eng.ms/docs/coreai/devdiv/one-engineering-system-1es/1es-docs/azure-artifacts/maven-credprovider #> [CmdletBinding()] @@ -31,15 +48,18 @@ $ErrorActionPreference = 'Stop' $groupId = 'com.microsoft.azure' $artifactId = 'artifacts-maven-credprovider' $bootstrapFeed = 'https://pkgs.dev.azure.com/artifacts-public/PublicTools/_packaging/AzureArtifacts/maven/v1' + +# Maven records the extension against this repository id. It must match the of the repositories +# declared in this repository's pom.xml files, otherwise resolution fails validation later. $repositoryId = 'central' + $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..' '..')).Path if (-not (Get-Command mvn -ErrorAction SilentlyContinue)) { - throw "Maven ('mvn') was not found on PATH." + throw "Maven ('mvn') was not found on PATH. Install Apache Maven 3.0 or above and try again." } -$customLocalRepository = -not [string]::IsNullOrWhiteSpace($LocalRepositoryPath) -if (-not $customLocalRepository) { +if (-not $LocalRepositoryPath) { $LocalRepositoryPath = Join-Path $HOME '.m2' 'repository' } @@ -47,26 +67,37 @@ $artifactDirectory = $LocalRepositoryPath foreach ($segment in ($groupId.Split('.') + @($artifactId, $Version))) { $artifactDirectory = Join-Path $artifactDirectory $segment } + $artifactPath = Join-Path $artifactDirectory "$artifactId-$Version.jar" -if ($Force -or -not (Test-Path $artifactPath)) { - $workingDirectory = Join-Path ([IO.Path]::GetTempPath()) ('maven-credprovider-' + [Guid]::NewGuid().ToString('n')) +if ((Test-Path $artifactPath) -and -not $Force) { + Write-Host "Credential provider $Version is already installed at '$artifactPath'." +} +else { + Write-Host "Installing credential provider $Version from the public tools feed..." + + # The bootstrap must run outside of any Maven project so that this repository's own repository + # and extension configuration does not take part in resolving the extension itself. + $workingDirectory = Join-Path ([IO.Path]::GetTempPath()) ('credprovider-bootstrap-' + [Guid]::NewGuid().ToString('n')) New-Item -ItemType Directory -Path $workingDirectory -Force | Out-Null + try { Push-Location $workingDirectory try { - $arguments = @( + $mvnArgs = @( '--batch-mode' 'dependency:get' "-Dartifact=${groupId}:${artifactId}:${Version}" "-DremoteRepositories=${repositoryId}::::${bootstrapFeed}" ) - if ($customLocalRepository) { - $arguments += "-Dmaven.repo.local=$LocalRepositoryPath" + + if ($PSBoundParameters.ContainsKey('LocalRepositoryPath')) { + $mvnArgs += "-Dmaven.repo.local=$LocalRepositoryPath" } - & mvn @arguments + + & mvn @mvnArgs if ($LASTEXITCODE -ne 0) { - throw "Maven failed with exit code $LASTEXITCODE." + throw "'mvn dependency:get' failed with exit code $LASTEXITCODE." } } finally { @@ -76,30 +107,40 @@ if ($Force -or -not (Test-Path $artifactPath)) { finally { Remove-Item $workingDirectory -Recurse -Force -ErrorAction SilentlyContinue } -} -if (-not (Test-Path $artifactPath)) { - throw "Credential provider was not found at '$artifactPath' after installation." + if (-not (Test-Path $artifactPath)) { + throw "Bootstrap reported success but '$artifactPath' was not found. If a mirror is configured in your settings.xml, temporarily disable it and retry." + } + + Write-Host "Installed credential provider to '$artifactPath'." } $extensionsDirectory = Join-Path $repoRoot '.mvn' $extensionsPath = Join-Path $extensionsDirectory 'extensions.xml' + if ((Test-Path $extensionsPath) -and -not $Force) { $existing = Get-Content $extensionsPath -Raw + if ($existing -notmatch [regex]::Escape($artifactId)) { - throw "'$extensionsPath' contains an unmanaged Maven extension. Use -Force to overwrite it." + throw "'$extensionsPath' already exists and declares extensions this script does not manage. Review it manually, or re-run with -Force to overwrite it." } + if ($existing -match "\s*$([regex]::Escape($Version))\s*") { - Write-Host "Maven credential provider $Version is already configured." + Write-Host "'$extensionsPath' is already configured for version $Version." + Write-Host 'Done.' return } } -$extensions = @" +$extensionsContent = @" - + + $groupId $artifactId @@ -109,5 +150,7 @@ $extensions = @" "@ New-Item -ItemType Directory -Path $extensionsDirectory -Force | Out-Null -Set-Content -Path $extensionsPath -Value $extensions -Encoding utf8 -Write-Host "Configured Maven credential provider $Version in '$extensionsPath'." \ No newline at end of file +Set-Content -Path $extensionsPath -Value $extensionsContent -Encoding utf8 + +Write-Host "Wrote '$extensionsPath' for version $Version." +Write-Host 'Done.' diff --git a/eng/scripts/install-maven-credprovider.sh b/eng/scripts/install-maven-credprovider.sh index 626b77e9..00135d40 100644 --- a/eng/scripts/install-maven-credprovider.sh +++ b/eng/scripts/install-maven-credprovider.sh @@ -1,88 +1,161 @@ #!/usr/bin/env bash +# +# Bootstraps the Azure Artifacts Maven credential provider for local development. +# +# Maven packages for this repository are restored from an Azure Artifacts feed. Reads are anonymous, +# so this script is only needed by Microsoft developers who have to ingest a package version that +# the feed has not cached yet. +# +# The script: +# 1. Verifies the credential provider is present in the local Maven repository, and downloads it +# from the public AzureArtifacts tools feed if it is not. +# 2. Writes '.mvn/extensions.xml' at the root of the repository so Maven loads the provider. +# +# '.mvn/' is intentionally listed in .gitignore. The extension exits when it detects a build context, +# and committing it would force an authenticated restore on anonymous consumers. Azure Pipelines +# uses the MavenAuthenticate@0 task instead. +# +# See https://eng.ms/docs/coreai/devdiv/one-engineering-system-1es/1es-docs/azure-artifacts/maven-credprovider set -euo pipefail -group_id='com.microsoft.azure' -artifact_id='artifacts-maven-credprovider' -bootstrap_feed='https://pkgs.dev.azure.com/artifacts-public/PublicTools/_packaging/AzureArtifacts/maven/v1' -repository_id='central' +GROUP_ID='com.microsoft.azure' +ARTIFACT_ID='artifacts-maven-credprovider' +BOOTSTRAP_FEED='https://pkgs.dev.azure.com/artifacts-public/PublicTools/_packaging/AzureArtifacts/maven/v1' + +# Maven records the extension against this repository id. It must match the of the repositories +# declared in this repository's pom.xml files, otherwise resolution fails validation later. +REPOSITORY_ID='central' + version='3.2.1' local_repository_path='' force=false usage() { - cat <<'EOF' + cat <<'EOF' Usage: install-maven-credprovider.sh [options] Options: - -v, --version Credential provider version to install. - -l, --local-repository Maven local repository path. - -f, --force Reinstall and overwrite .mvn/extensions.xml. - -h, --help Show this help text. + -v, --version Version of the credential provider to install. + -l, --local-repository Path to the local Maven repository. Defaults to ~/.m2/repository. + -f, --force Overwrite an unmanaged .mvn/extensions.xml and re-download the + credential provider even when it is already installed. + -h, --help Show this help text. EOF } while [[ $# -gt 0 ]]; do - case "$1" in - -v|--version) [[ $# -ge 2 ]] || { echo "error: $1 requires a value" >&2; exit 1; }; version="$2"; shift 2 ;; - -l|--local-repository) [[ $# -ge 2 ]] || { echo "error: $1 requires a value" >&2; exit 1; }; local_repository_path="$2"; shift 2 ;; - -f|--force) force=true; shift ;; - -h|--help) usage; exit 0 ;; - *) echo "error: unknown argument '$1'" >&2; usage >&2; exit 1 ;; - esac + case "$1" in + -v|--version) + [[ $# -ge 2 ]] || { echo "error: $1 requires a value" >&2; exit 1; } + version="$2" + shift 2 + ;; + -l|--local-repository) + [[ $# -ge 2 ]] || { echo "error: $1 requires a value" >&2; exit 1; } + local_repository_path="$2" + shift 2 + ;; + -f|--force) + force=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "error: unknown argument '$1'" >&2 + usage >&2 + exit 1 + ;; + esac done -command -v mvn >/dev/null 2>&1 || { echo "error: Maven ('mvn') was not found on PATH." >&2; exit 1; } - script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" repo_root="$(cd -- "$script_dir/../.." && pwd)" -custom_local_repository=true -if [[ -z "$local_repository_path" ]]; then - custom_local_repository=false - local_repository_path="$HOME/.m2/repository" + +if ! command -v mvn >/dev/null 2>&1; then + echo "error: Maven ('mvn') was not found on PATH. Install Apache Maven 3.0 or above and try again." >&2 + exit 1 fi -group_path="${group_id//./\/}" -artifact_path="$local_repository_path/$group_path/$artifact_id/$version/$artifact_id-$version.jar" -if [[ "$force" == true || ! -f "$artifact_path" ]]; then - working_directory="$(mktemp -d)" - trap 'rm -rf "$working_directory"' EXIT - arguments=( - --batch-mode - dependency:get - "-Dartifact=${group_id}:${artifact_id}:${version}" - "-DremoteRepositories=${repository_id}::::${bootstrap_feed}" - ) - if [[ "$custom_local_repository" == true ]]; then - arguments+=("-Dmaven.repo.local=$local_repository_path") - fi - (cd "$working_directory" && mvn "${arguments[@]}") +local_repository_specified=true +if [[ -z "$local_repository_path" ]]; then + local_repository_specified=false + local_repository_path="$HOME/.m2/repository" fi -[[ -f "$artifact_path" ]] || { echo "error: credential provider was not found at '$artifact_path'." >&2; exit 1; } +group_path="${GROUP_ID//./\/}" +artifact_path="$local_repository_path/$group_path/$ARTIFACT_ID/$version/$ARTIFACT_ID-$version.jar" + +if [[ -f "$artifact_path" && "$force" != true ]]; then + echo "Credential provider $version is already installed at '$artifact_path'." +else + echo "Installing credential provider $version from the public tools feed..." + + # The bootstrap must run outside of any Maven project so that this repository's own repository + # and extension configuration does not take part in resolving the extension itself. + working_directory="$(mktemp -d)" + cleanup() { rm -rf "$working_directory"; } + trap cleanup EXIT + + mvn_args=( + --batch-mode + dependency:get + "-Dartifact=${GROUP_ID}:${ARTIFACT_ID}:${version}" + "-DremoteRepositories=${REPOSITORY_ID}::::${BOOTSTRAP_FEED}" + ) + + if [[ "$local_repository_specified" == true ]]; then + mvn_args+=("-Dmaven.repo.local=$local_repository_path") + fi + + (cd "$working_directory" && mvn "${mvn_args[@]}") + + if [[ ! -f "$artifact_path" ]]; then + echo "error: bootstrap reported success but '$artifact_path' was not found." >&2 + echo "If a mirror is configured in your settings.xml, temporarily disable it and retry." >&2 + exit 1 + fi + + echo "Installed credential provider to '$artifact_path'." +fi extensions_directory="$repo_root/.mvn" extensions_path="$extensions_directory/extensions.xml" + if [[ -f "$extensions_path" && "$force" != true ]]; then - grep -q "$artifact_id" "$extensions_path" || { echo "error: '$extensions_path' contains an unmanaged Maven extension." >&2; exit 1; } - if grep -qE "[[:space:]]*${version//./\.}[[:space:]]*" "$extensions_path"; then - echo "Maven credential provider $version is already configured." - exit 0 - fi + if ! grep -q "$ARTIFACT_ID" "$extensions_path"; then + echo "error: '$extensions_path' already exists and declares extensions this script does not manage." >&2 + echo "Review it manually, or re-run with --force to overwrite it." >&2 + exit 1 + fi + + if grep -qE "[[:space:]]*${version//./\\.}[[:space:]]*" "$extensions_path"; then + echo "'$extensions_path' is already configured for version $version." + echo 'Done.' + exit 0 + fi fi mkdir -p "$extensions_directory" cat >"$extensions_path" < - + + - $group_id - $artifact_id + $GROUP_ID + $ARTIFACT_ID $version EOF -echo "Configured Maven credential provider $version in '$extensions_path'." \ No newline at end of file +echo "Wrote '$extensions_path' for version $version." +echo 'Done.' diff --git a/pom.xml b/pom.xml index c5413885..6f10206f 100644 --- a/pom.xml +++ b/pom.xml @@ -459,4 +459,4 @@ - \ No newline at end of file + diff --git a/settings.xml b/settings.xml index dd6fa56a..dca7f6fd 100644 --- a/settings.xml +++ b/settings.xml @@ -22,4 +22,4 @@ central - \ No newline at end of file + From 3bd032e57e67f67af1323139294c6e76fde9bd87 Mon Sep 17 00:00:00 2001 From: AzureFunctionsJava Date: Thu, 6 Aug 2026 14:27:43 -0500 Subject: [PATCH 3/3] Remove noncompliant certificate download --- .gitattributes | 2 +- .../jobs/run-emulated-tests-linux.yml | 5 +- .../jobs/run-emulated-tests-windows.yml | 5 +- .../official/jobs/run-e2e-tests-linux.yml | 5 +- .../official/jobs/run-e2e-tests-windows.yml | 5 +- eng/scripts/Export-JavaCacerts.ps1 | 51 +++++++++++++++++++ samples/spring-cloud-example/pom.xml | 2 +- 7 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 eng/scripts/Export-JavaCacerts.ps1 diff --git a/.gitattributes b/.gitattributes index 28fc1d67..c275e0cf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,7 @@ # Shell scripts must retain LF endings on every platform. *.sh text eol=lf -# Preserve the existing CRLF convention in these standalone sample projects. +# Treat CRLF terminators in these standalone sample projects as line endings during whitespace checks. warmup-function/pom.xml whitespace=cr-at-eol samples/dependency-injection-example/dagger-function/pom.xml whitespace=cr-at-eol samples/dependency-injection-example/guice-function/pom.xml whitespace=cr-at-eol diff --git a/eng/ci/templates/jobs/run-emulated-tests-linux.yml b/eng/ci/templates/jobs/run-emulated-tests-linux.yml index 3dcd0fa0..edb97da5 100644 --- a/eng/ci/templates/jobs/run-emulated-tests-linux.yml +++ b/eng/ci/templates/jobs/run-emulated-tests-linux.yml @@ -127,12 +127,11 @@ jobs: .\package-pipeline.ps1 -buildNumber $buildNumber displayName: 'Executing build script' - pwsh: | - # Download Mozilla CA certificate bundle - Invoke-WebRequest -Uri "https://curl.se/ca/cacert.pem" -OutFile "./emulatedtests/confluent_cloud_cacert.pem" + ./eng/scripts/Export-JavaCacerts.ps1 -OutputPath "./emulatedtests/confluent_cloud_cacert.pem" cd ./emulatedtests mvn clean package `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B Copy-Item "confluent_cloud_cacert.pem" "./target/azure-functions/azure-functions-java-emulatedtests" - displayName: 'Download CA Bundle & Package Java for Emulated Tests' + displayName: 'Export CA Bundle & Package Java for Emulated Tests' - task: npmAuthenticate@0 inputs: workingFile: '$(Build.SourcesDirectory)/.npmrc' diff --git a/eng/ci/templates/jobs/run-emulated-tests-windows.yml b/eng/ci/templates/jobs/run-emulated-tests-windows.yml index 66d6e222..4c8490d6 100644 --- a/eng/ci/templates/jobs/run-emulated-tests-windows.yml +++ b/eng/ci/templates/jobs/run-emulated-tests-windows.yml @@ -108,12 +108,11 @@ jobs: .\package-pipeline.ps1 -buildNumber $buildNumber displayName: 'Executing build script' - pwsh: | - # Download Mozilla CA certificate bundle - Invoke-WebRequest -Uri "https://curl.se/ca/cacert.pem" -OutFile "./emulatedtests/confluent_cloud_cacert.pem" + ./eng/scripts/Export-JavaCacerts.ps1 -OutputPath "./emulatedtests/confluent_cloud_cacert.pem" cd ./emulatedtests mvn clean package `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B Copy-Item "confluent_cloud_cacert.pem" "./target/azure-functions/azure-functions-java-emulatedtests" - displayName: 'Download CA Bundle & Package Java for Emulated Tests' + displayName: 'Export CA Bundle & Package Java for Emulated Tests' - task: npmAuthenticate@0 inputs: workingFile: '$(Build.SourcesDirectory)/.npmrc' diff --git a/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml b/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml index 6e879df6..e6736561 100644 --- a/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml +++ b/eng/ci/templates/official/jobs/run-e2e-tests-linux.yml @@ -111,12 +111,11 @@ jobs: func --version displayName: 'Setup Core Tools - Linux' - pwsh: | - # Download Mozilla CA certificate bundle - Invoke-WebRequest -Uri "https://curl.se/ca/cacert.pem" -OutFile "./endtoendtests/confluent_cloud_cacert.pem" + ./eng/scripts/Export-JavaCacerts.ps1 -OutputPath "./endtoendtests/confluent_cloud_cacert.pem" cd ./endtoendtests mvn clean package `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B Copy-Item "confluent_cloud_cacert.pem" "./target/azure-functions/azure-functions-java-endtoendtests" - displayName: 'Download CA Bundle & Package Java for E2E' + displayName: 'Export CA Bundle & Package Java for E2E' - task: NuGetAuthenticate@1 displayName: 'Authenticate NuGet to CFS' - task: DotNetCoreCLI@2 diff --git a/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml b/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml index 03bd1f51..74989631 100644 --- a/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml +++ b/eng/ci/templates/official/jobs/run-e2e-tests-windows.yml @@ -96,12 +96,11 @@ jobs: func --version displayName: 'Setup Core Tools - Windows' - pwsh: | - # Download Mozilla CA certificate bundle - Invoke-WebRequest -Uri "https://curl.se/ca/cacert.pem" -OutFile "./endtoendtests/confluent_cloud_cacert.pem" + ./eng/scripts/Export-JavaCacerts.ps1 -OutputPath "./endtoendtests/confluent_cloud_cacert.pem" cd ./endtoendtests mvn clean package `-Dmaven`.javadoc`.skip=true `-Dmaven`.test`.skip `-Dorg`.slf4j`.simpleLogger`.log`.org`.apache`.maven`.cli`.transfer`.Slf4jMavenTransferListener=warn `-B Copy-Item "confluent_cloud_cacert.pem" "./target/azure-functions/azure-functions-java-endtoendtests" - displayName: 'Download CA Bundle & Package Java for E2E' + displayName: 'Export CA Bundle & Package Java for E2E' - task: NuGetAuthenticate@1 displayName: 'Authenticate NuGet to CFS' - task: DotNetCoreCLI@2 diff --git a/eng/scripts/Export-JavaCacerts.ps1 b/eng/scripts/Export-JavaCacerts.ps1 new file mode 100644 index 00000000..87ade462 --- /dev/null +++ b/eng/scripts/Export-JavaCacerts.ps1 @@ -0,0 +1,51 @@ +#!/usr/bin/env pwsh + +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [string] $OutputPath +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$keytool = Get-Command keytool -ErrorAction Stop +$javaHome = if ($env:JAVA_HOME) { + $env:JAVA_HOME +} +else { + Split-Path (Split-Path $keytool.Source -Parent) -Parent +} + +$trustStore = @( + (Join-Path $javaHome 'lib/security/cacerts') + (Join-Path $javaHome 'jre/lib/security/cacerts') +) | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1 + +if (-not $trustStore) { + throw "Could not find the Java cacerts trust store under '$javaHome'." +} + +$keytoolOutput = & $keytool.Source -list -rfc -keystore $trustStore -storepass changeit 2>&1 +if ($LASTEXITCODE -ne 0) { + throw "keytool failed with exit code $LASTEXITCODE.`n$($keytoolOutput -join [Environment]::NewLine)" +} + +$certificates = [regex]::Matches( + ($keytoolOutput -join "`n"), + '-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----', + [Text.RegularExpressions.RegexOptions]::Singleline) + +if ($certificates.Count -eq 0) { + throw "No PEM certificates were exported from '$trustStore'." +} + +$resolvedOutputPath = [IO.Path]::GetFullPath($OutputPath) +$outputDirectory = Split-Path $resolvedOutputPath -Parent +New-Item -ItemType Directory -Path $outputDirectory -Force | Out-Null +[IO.File]::WriteAllText( + $resolvedOutputPath, + (($certificates.Value -join "`n") + "`n"), + [Text.UTF8Encoding]::new($false)) + +Write-Host "Exported $($certificates.Count) Java root certificates to '$resolvedOutputPath'." \ No newline at end of file diff --git a/samples/spring-cloud-example/pom.xml b/samples/spring-cloud-example/pom.xml index c84907a8..19d9dfa1 100644 --- a/samples/spring-cloud-example/pom.xml +++ b/samples/spring-cloud-example/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.0.1-SNAPSHOT + 3.0.1