From b8ad3142edb2fd44153ae4feeeacfa84fc251a39 Mon Sep 17 00:00:00 2001 From: Haoling Dong Date: Sat, 14 Feb 2026 15:40:38 +0800 Subject: [PATCH 1/5] inner loop, add update-meta-data script --- eng/scripts/Automation-Sdk-UpdateMetadata.ps1 | 88 ++ eng/scripts/helpers/Metadata-Helpers.ps1 | 673 +++++++++++++ .../Automation-Sdk-UpdateMetadata.tests.ps1 | 951 ++++++++++++++++++ eng/swagger_to_sdk_config.json | 3 + 4 files changed, 1715 insertions(+) create mode 100644 eng/scripts/Automation-Sdk-UpdateMetadata.ps1 create mode 100644 eng/scripts/helpers/Metadata-Helpers.ps1 create mode 100644 eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 diff --git a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 new file mode 100644 index 000000000000..4f0f610fe70d --- /dev/null +++ b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 @@ -0,0 +1,88 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +<# +.SYNOPSIS + Updates parent-level and root-level pom.xml files and ci.yml for a Java SDK package. + +.DESCRIPTION + This script handles two cases: + Case 1 - Service exists, new resourcemanager package: + - Skips root pom (service already listed) + - Updates service-level pom.xml with new module + - Updates existing ci.yml with new artifact entry + Case 2 - Brand new service: + - Updates root pom.xml with new service module + - Creates service-level pom.xml from template + - Skips ci.yml (creation handled by a separate script) + +.PARAMETER PackagePath + Absolute path to the root folder of the local SDK project (containing pom.xml). + +.PARAMETER SdkRepoPath + Absolute path to the root folder of the local SDK repository. + +.EXAMPLE + .\Automation-Sdk-UpdateMetadata.ps1 -PackagePath "C:\repos\azure-sdk-for-java\sdk\network\azure-resourcemanager-network" -SdkRepoPath "C:\repos\azure-sdk-for-java" +#> + +param( + [Parameter(Mandatory = $true)] + [ValidateScript({Test-Path $_})] + [string]$PackagePath, + + [Parameter(Mandatory = $true)] + [ValidateScript({Test-Path $_})] + [string]$SdkRepoPath +) + +$ErrorActionPreference = "Stop" + +# Import common scripts for logging functions +$commonScriptPath = Join-Path $PSScriptRoot ".." "common" "scripts" "common.ps1" +. $commonScriptPath + +# Import metadata helper functions +$helperPath = Join-Path $PSScriptRoot "helpers" "Metadata-Helpers.ps1" +. $helperPath + +try { + LogInfo "========================================" + LogInfo "Azure SDK Metadata Update Tool" + LogInfo "========================================" + LogInfo "" + + LogInfo "Step 1: Deriving service and module from package path..." + $pathInfo = Get-ServiceAndModuleFromPath -PackagePath $PackagePath -SdkRepoPath $SdkRepoPath + $service = $pathInfo.Service + $module = $pathInfo.Module + LogInfo " Service: $service" + LogInfo " Module: $module" + LogInfo "" + + LogInfo "Step 2: Reading groupId from package POM..." + $pomPath = Join-Path $PackagePath "pom.xml" + $groupId = Get-GroupIdFromPom -PomPath $pomPath + LogInfo " Group ID: $groupId" + LogInfo "" + + LogInfo "Step 3: Updating root pom.xml..." + Update-RootPom -SdkRepoPath $SdkRepoPath -Service $service + LogInfo "" + + LogInfo "Step 4: Updating service-level pom.xml..." + Update-ServicePom -SdkRepoPath $SdkRepoPath -Service $service -Module $module + LogInfo "" + + LogInfo "Step 5: Updating ci.yml..." + Update-CiYml -SdkRepoPath $SdkRepoPath -Service $service -Module $module -GroupId $groupId + LogInfo "" + + LogInfo "✅ Metadata updated successfully!" + exit 0 +} +catch { + LogError "An error occurred: $_" + LogError "Stack trace: $($_.ScriptStackTrace)" + exit 1 +} diff --git a/eng/scripts/helpers/Metadata-Helpers.ps1 b/eng/scripts/helpers/Metadata-Helpers.ps1 new file mode 100644 index 000000000000..2a33f07a2fe5 --- /dev/null +++ b/eng/scripts/helpers/Metadata-Helpers.ps1 @@ -0,0 +1,673 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +<# +.SYNOPSIS + Helper functions for metadata update automation. + +.DESCRIPTION + This file provides helper functions for updating parent-level and root-level + pom.xml files, as well as ci.yml files for Java SDK packages. + Logic is ported from eng/automation/utils.py (Python). +#> + +function Get-ServiceAndModuleFromPath { + <# + .SYNOPSIS + Derives service name and module (artifact) name from a package path. + + .DESCRIPTION + Given a package path like "C:\repos\azure-sdk-for-java\sdk\network\azure-resourcemanager-network", + extracts the service name ("network") and module name ("azure-resourcemanager-network"). + + .PARAMETER PackagePath + Absolute path to the package directory. + + .PARAMETER SdkRepoPath + Absolute path to the SDK repository root. + + .OUTPUTS + Hashtable with Service and Module properties. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$PackagePath, + + [Parameter(Mandatory = $true)] + [string]$SdkRepoPath + ) + + # Normalize paths to forward slashes for consistent comparison + $normalizedPackagePath = $PackagePath.TrimEnd('\', '/').Replace('\', '/') + $normalizedSdkRepoPath = $SdkRepoPath.TrimEnd('\', '/').Replace('\', '/') + + $sdkDir = "$normalizedSdkRepoPath/sdk/" + if (-not $normalizedPackagePath.StartsWith($sdkDir)) { + throw "PackagePath '$PackagePath' is not under sdk/ directory of '$SdkRepoPath'. Expected format: {SdkRepoPath}/sdk/{service}/{module}" + } + + $relativePath = $normalizedPackagePath.Substring($sdkDir.Length) + $parts = $relativePath.Split('/') + if ($parts.Count -lt 2) { + throw "Cannot determine service and module from path: $PackagePath. Expected format: sdk/{service}/{module}" + } + + return @{ + Service = $parts[0] + Module = $parts[1] + } +} + +function Get-GroupIdFromPom { + <# + .SYNOPSIS + Extracts groupId from a Maven POM file. + + .PARAMETER PomPath + The absolute path to the pom.xml file. + + .OUTPUTS + String representing the groupId. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ValidateScript({Test-Path $_})] + [string]$PomPath + ) + + [xml]$pomXml = Get-Content $PomPath + $groupId = $pomXml.project.groupId + + # If groupId is not in the current project, check parent + if ([string]::IsNullOrEmpty($groupId)) { + $groupId = $pomXml.project.parent.groupId + } + + if ([string]::IsNullOrEmpty($groupId)) { + throw "Could not extract groupId from POM file: $PomPath" + } + + return $groupId +} + +# Equivalent of Python add_module_to_modules() +function Add-ModuleToModulesBlock { + <# + .SYNOPSIS + Adds a module entry to a XML block, sorted alphabetically. + + .PARAMETER ModulesBlock + The existing ... XML string. + + .PARAMETER Module + The module name to add. + + .OUTPUTS + String representing the updated block. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$ModulesBlock, + + [Parameter(Mandatory = $true)] + [string]$Module + ) + + # Extract indent from the closing tag (matches Python: re.search(r"([^\S\n\r]*)", modules)) + if ($ModulesBlock -match '([^\S\n\r]*)') { + $closingIndent = $matches[1] + $indent = $closingIndent + " " + } + else { + $closingIndent = " " + $indent = " " + } + + # Collect all existing modules into a set and add new one + $allModules = [System.Collections.Generic.HashSet[string]]::new() + $moduleMatches = [regex]::Matches($ModulesBlock, '(.*?)') + foreach ($m in $moduleMatches) { + [void]$allModules.Add($m.Groups[1].Value) + } + [void]$allModules.Add($Module) + + # Sort and build module lines (matches Python: indent + POM_MODULE_FORMAT.format(module)) + $sortedModules = $allModules | Sort-Object + $moduleLines = ($sortedModules | ForEach-Object { "${indent}$_" }) -join "`n" + + return "`n${moduleLines}`n${closingIndent}" +} + +# Equivalent of Python add_module_to_default_profile() +function Add-ModuleToDefaultProfile { + <# + .SYNOPSIS + Adds a module to the default profile's section in a POM with profiles. + + .PARAMETER PomContent + The full content of the POM file. + + .PARAMETER Module + The module name to add. + + .OUTPUTS + Hashtable with Success (boolean) and Content (string) properties. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$PomContent, + + [Parameter(Mandatory = $true)] + [string]$Module + ) + + $profileMatches = [regex]::Matches($PomContent, '(?s).*?') + foreach ($profileMatch in $profileMatches) { + $profileValue = $profileMatch.Value + if ($profileValue -match 'default') { + if (([regex]::Matches($profileValue, '')).Count -gt 1) { + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[POM][Profile][Skip] find more than one in default" + } + return @{ Success = $false; Content = "" } + } + + $modulesMatch = [regex]::Match($profileValue, '(?s).*') + if (-not $modulesMatch.Success) { + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[POM][Profile][Skip] Cannot find in default" + } + return @{ Success = $false; Content = "" } + } + + $updatedModules = Add-ModuleToModulesBlock -ModulesBlock $modulesMatch.Value -Module $Module + + # Calculate absolute position (matches Python: pom[: profile.start() + modules.start()]) + $absStart = $profileMatch.Index + $modulesMatch.Index + $absEnd = $absStart + $modulesMatch.Length + $updatedPom = $PomContent.Substring(0, $absStart) + $updatedModules + $PomContent.Substring($absEnd) + + return @{ Success = $true; Content = $updatedPom } + } + } + + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[POM][Profile][Skip] cannot find with default" + } + return @{ Success = $false; Content = "" } +} + +# Equivalent of Python add_module_to_pom() +function Add-ModuleToPom { + <# + .SYNOPSIS + Adds a module entry to a POM file's section. + + .DESCRIPTION + Handles POMs with single blocks and POMs with containing + a default profile with its own block. + + .PARAMETER PomContent + The full content of the POM file as a string. + + .PARAMETER Module + The module name to add. + + .OUTPUTS + Hashtable with Success (boolean) and Content (string) properties. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$PomContent, + + [Parameter(Mandatory = $true)] + [string]$Module + ) + + # Check if module already exists + if ($PomContent.Contains("$Module")) { + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[POM][Skip] pom already has module $Module" + } + return @{ Success = $true; Content = $PomContent } + } + + # Count blocks + $modulesCount = ([regex]::Matches($PomContent, '')).Count + + if ($modulesCount -gt 1) { + if ($PomContent.Contains('')) { + return Add-ModuleToDefaultProfile -PomContent $PomContent -Module $Module + } + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[POM][Skip] find more than one in pom" + } + return @{ Success = $false; Content = "" } + } + + # Find the single block + $modulesMatch = [regex]::Match($PomContent, '(?s).*?') + if (-not $modulesMatch.Success) { + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[POM][Skip] Cannot find in pom" + } + return @{ Success = $false; Content = "" } + } + + $updatedModules = Add-ModuleToModulesBlock -ModulesBlock $modulesMatch.Value -Module $Module + $updatedPom = $PomContent.Substring(0, $modulesMatch.Index) + $updatedModules + $PomContent.Substring($modulesMatch.Index + $modulesMatch.Length) + + return @{ Success = $true; Content = $updatedPom } +} + +# Equivalent of Python update_root_pom() +function Update-RootPom { + <# + .SYNOPSIS + Adds sdk/{service} as a module to the root pom.xml. + + .PARAMETER SdkRepoPath + Absolute path to the SDK repository root. + + .PARAMETER Service + The service directory name (e.g., "network"). + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$SdkRepoPath, + + [Parameter(Mandatory = $true)] + [string]$Service + ) + + $pomFile = Join-Path $SdkRepoPath "pom.xml" + if (-not (Test-Path $pomFile)) { + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[POM][Skip] cannot find root pom" + } + return + } + + $module = "sdk/$Service" + $pomContent = Get-Content -Path $pomFile -Raw + + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[POM][Process] dealing with root pom" + } + + $result = Add-ModuleToPom -PomContent $pomContent -Module $module + if ($result.Success) { + Set-Content -Path $pomFile -Value $result.Content -NoNewline + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[POM][Success] Write to root pom" + } + } +} + +# Equivalent of the POM update part of Python update_service_files_for_new_lib() +function Update-ServicePom { + <# + .SYNOPSIS + Adds a module to the service-level pom.xml, creating it if it doesn't exist. + + .PARAMETER SdkRepoPath + Absolute path to the SDK repository root. + + .PARAMETER Service + The service directory name. + + .PARAMETER Module + The artifact/module name to add. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$SdkRepoPath, + + [Parameter(Mandatory = $true)] + [string]$Service, + + [Parameter(Mandatory = $true)] + [string]$Module + ) + + $pomFile = Join-Path $SdkRepoPath "sdk" $Service "pom.xml" + + if (Test-Path $pomFile) { + $pomContent = Get-Content -Path $pomFile -Raw + } + else { + # Create from template (matches Python POM_FORMAT) + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[POM][Process] creating new service pom.xml" + } + $pomContent = @" + + + 4.0.0 + com.azure + azure-${Service}-service + pom + 1.0.0 + + + ${Module} + + +"@ + $pomDir = Split-Path $pomFile -Parent + if (-not (Test-Path $pomDir)) { + New-Item -ItemType Directory -Path $pomDir -Force | Out-Null + } + Set-Content -Path $pomFile -Value $pomContent -NoNewline + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[POM][Success] Created new service pom.xml at: $pomFile" + } + return + } + + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[POM][Process] dealing with service pom.xml" + } + + $result = Add-ModuleToPom -PomContent $pomContent -Module $Module + if ($result.Success) { + Set-Content -Path $pomFile -Value $result.Content -NoNewline + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[POM][Success] Write to service pom.xml" + } + } +} + +# Equivalent of the CI update part of Python update_service_files_for_new_lib() +function Update-CiYml { + <# + .SYNOPSIS + Adds an artifact entry to the service ci.yml file, creating it if it doesn't exist. + + .DESCRIPTION + Follows the same logic as Python update_service_files_for_new_lib() for ci.yml: + - If ci.yml doesn't exist, create from template + - If ci.yml exists with SDKType=data, rename to ci.data.yml and create new ci.yml + - If artifact already exists, skip + - Otherwise add the artifact (with release parameter) + Uses text-based operations to preserve the existing YAML format. + + .PARAMETER SdkRepoPath + Absolute path to the SDK repository root. + + .PARAMETER Service + The service directory name. + + .PARAMETER Module + The artifact/module name. + + .PARAMETER GroupId + The Maven groupId for the artifact. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$SdkRepoPath, + + [Parameter(Mandatory = $true)] + [string]$Service, + + [Parameter(Mandatory = $true)] + [string]$Module, + + [Parameter(Mandatory = $true)] + [string]$GroupId + ) + + $ciFile = Join-Path $SdkRepoPath "sdk" $Service "ci.yml" + $safeName = $Module -replace '-', '' + $isMgmt = $Module -match '-resourcemanager-' + $releaseDefault = if ($isMgmt) { "false" } else { "true" } + $releaseParameterName = "release_$safeName" + + if (-not (Test-Path $ciFile)) { + # ci.yml creation for new services is handled by a separate script + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Skip] ci.yml does not exist, skipping (new service ci.yml creation is handled separately)" + } + return + } + + # Read existing ci.yml + $existingContent = Get-Content -Path $ciFile -Raw + + # Check if artifact already exists + if ($existingContent -match "name:\s+$([regex]::Escape($Module))\s") { + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Skip] ci.yml already has artifact $Module" + } + return + } + + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Process] updating ci.yml with artifact $Module" + } + + # Add path entries to trigger and pr includes/excludes + $updatedContent = Add-PathToCiYml -CiContent $existingContent -Service $Service -Module $Module + + # Add release parameter before "extends:" line + $paramBlock = "- name: ${releaseParameterName}`n displayName: '${Module}'`n type: boolean`n default: ${releaseDefault}`n`n" + + $extendsIdx = $updatedContent.IndexOf("`nextends:") + if ($extendsIdx -ge 0) { + $extendsIdx += 1 # skip the newline + $updatedContent = $updatedContent.Substring(0, $extendsIdx) + $paramBlock + $updatedContent.Substring($extendsIdx) + } + elseif ($updatedContent.IndexOf("extends:") -ge 0) { + $extendsIdx = $updatedContent.IndexOf("extends:") + $updatedContent = $updatedContent.Substring(0, $extendsIdx) + $paramBlock + $updatedContent.Substring($extendsIdx) + } + + # Add artifact entry at end of file + $artifactBlock = " - name: ${Module}`n groupId: ${GroupId}`n safeName: ${SafeName}`n releaseInBatch: `${{ parameters.${releaseParameterName} }}" + $updatedContent = $updatedContent.TrimEnd() + "`n" + $artifactBlock + "`n" + + Set-Content -Path $ciFile -Value $updatedContent -NoNewline + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Success] Updated ci.yml with artifact $Module" + } +} + +function Write-NewCiYmlFile { + <# + .SYNOPSIS + Creates a brand-new ci.yml file from scratch. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][string]$CiFile, + [Parameter(Mandatory = $true)][string]$Service, + [Parameter(Mandatory = $true)][string]$Module, + [Parameter(Mandatory = $true)][string]$GroupId, + [Parameter(Mandatory = $true)][string]$SafeName, + [Parameter(Mandatory = $true)][string]$ReleaseParameterName, + [Parameter(Mandatory = $true)][string]$ReleaseDefault + ) + + $content = @" +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - hotfix/* + - release/* + paths: + include: + - sdk/${Service}/ci.yml + - sdk/${Service}/${Module}/ + exclude: + - sdk/${Service}/pom.xml + - sdk/${Service}/${Module}/pom.xml + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/${Service}/ci.yml + - sdk/${Service}/${Module}/ + exclude: + - sdk/${Service}/pom.xml + - sdk/${Service}/${Module}/pom.xml + +parameters: +- name: ${ReleaseParameterName} + displayName: '${Module}' + type: boolean + default: ${ReleaseDefault} + +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: ${Service} + Artifacts: + - name: ${Module} + groupId: ${GroupId} + safeName: ${SafeName} + releaseInBatch: `${{ parameters.${ReleaseParameterName} }} +"@ + + $ciDir = Split-Path $CiFile -Parent + if (-not (Test-Path $ciDir)) { + New-Item -ItemType Directory -Path $ciDir -Force | Out-Null + } + Set-Content -Path $CiFile -Value $content + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Success] Created new ci.yml at: $CiFile" + } +} + +function Add-PathToCiYml { + <# + .SYNOPSIS + Adds include/exclude paths for a module to both trigger and pr sections. + + .PARAMETER CiContent + The ci.yml content as a string. + + .PARAMETER Service + The service directory name. + + .PARAMETER Module + The module name. + + .OUTPUTS + Updated ci.yml content string. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$CiContent, + + [Parameter(Mandatory = $true)] + [string]$Service, + + [Parameter(Mandatory = $true)] + [string]$Module + ) + + $includeEntry = " - sdk/${Service}/${Module}/" + $excludeEntry = " - sdk/${Service}/${Module}/pom.xml" + $modulePathEscaped = [regex]::Escape("sdk/${Service}/${Module}/") + + $lines = $CiContent -split "`n" + $newLines = [System.Collections.Generic.List[string]]::new() + + $inSection = "" # "trigger" or "pr" + $inPaths = $false + $inInclude = $false + $inExclude = $false + $addedInclude = $false + $addedExclude = $false + $hasModuleInclude = $CiContent -match $modulePathEscaped + + for ($i = 0; $i -lt $lines.Count; $i++) { + $line = $lines[$i] + $trimmed = $line.Trim() + + # Track which top-level section we're in + if ($line -match '^trigger:') { $inSection = "trigger"; $addedInclude = $false; $addedExclude = $false } + elseif ($line -match '^pr:') { $inSection = "pr"; $addedInclude = $false; $addedExclude = $false } + elseif ($line -match '^[a-zA-Z]') { + if ($trimmed -ne 'trigger:' -and $trimmed -ne 'pr:') { + $inSection = "" + $inPaths = $false + $inInclude = $false + $inExclude = $false + } + } + + if ($inSection -ne "" -and $trimmed -eq 'paths:') { $inPaths = $true } + if ($inPaths -and $trimmed -eq 'include:') { $inInclude = $true; $inExclude = $false } + if ($inPaths -and $trimmed -eq 'exclude:') { $inExclude = $true; $inInclude = $false } + + # Detect end of include block + if ($inInclude -and -not $addedInclude -and $trimmed -match '^-') { + $nextIdx = $i + 1 + if ($nextIdx -lt $lines.Count) { + $nextTrimmed = $lines[$nextIdx].Trim() + if ($nextTrimmed -eq 'exclude:' -or ($nextTrimmed -ne '' -and $nextTrimmed -notmatch '^-')) { + if (-not $hasModuleInclude) { + $newLines.Add($line) + $newLines.Add($includeEntry) + $addedInclude = $true + continue + } + } + } + } + + # Detect end of exclude block + if ($inExclude -and -not $addedExclude -and $trimmed -match '^-') { + $nextIdx = $i + 1 + $isLastExcludeLine = $false + if ($nextIdx -lt $lines.Count) { + $nextTrimmed = $lines[$nextIdx].Trim() + if ($nextTrimmed -eq '' -or ($nextTrimmed -notmatch '^-')) { + $isLastExcludeLine = $true + } + } + else { + $isLastExcludeLine = $true + } + + if ($isLastExcludeLine) { + $moduleExcludeEscaped = [regex]::Escape("sdk/${Service}/${Module}/pom.xml") + if ($CiContent -notmatch $moduleExcludeEscaped) { + $newLines.Add($line) + $newLines.Add($excludeEntry) + $addedExclude = $true + $inExclude = $false + $inPaths = $false + continue + } + } + } + + $newLines.Add($line) + } + + return $newLines -join "`n" +} diff --git a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 new file mode 100644 index 000000000000..1ce70ceb3e2a --- /dev/null +++ b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 @@ -0,0 +1,951 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +<# +.SYNOPSIS + Pester tests for Automation-Sdk-UpdateMetadata.ps1 + +.DESCRIPTION + This file contains unit tests for the metadata update automation functions + using the Pester testing framework. + +.NOTES + How to run: + 1. Install Pester if not already installed: + Install-Module Pester -Force -MinimumVersion 5.3.3 + + 2. Run the tests: + Invoke-Pester ./Automation-Sdk-UpdateMetadata.tests.ps1 +#> + +BeforeAll { + # Import metadata helper functions + $helperPath = Join-Path $PSScriptRoot ".." "helpers" "Metadata-Helpers.ps1" + . $helperPath + + # Create a test directory structure + $script:TestRoot = Join-Path ([System.IO.Path]::GetTempPath()) "MetadataAutomationTests_$(New-Guid)" + New-Item -ItemType Directory -Path $script:TestRoot -Force | Out-Null + + # Sample POM with direct groupId + $script:SamplePomXml = @" + + + 4.0.0 + com.azure.resourcemanager + azure-resourcemanager-network + 2.0.0 + +"@ + + # Sample POM with groupId inherited from parent + $script:SamplePomWithParentXml = @" + + + 4.0.0 + + com.azure + azure-client-sdk-parent + 1.7.0 + + azure-storage-blob + 12.0.0 + +"@ + + # Sample service aggregator POM + $script:SampleServicePomXml = @" + + + 4.0.0 + com.azure + azure-network-service + pom + 1.0.0 + + + azure-resourcemanager-network + + +"@ + + # Sample root POM + $script:SampleRootPomXml = @" + + + sdk/advisor + sdk/network + sdk/storage + + +"@ + + # Sample ci.yml (single-artifact management library) + $script:SampleCiYml = @" +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - hotfix/* + - release/* + paths: + include: + - sdk/network/ + exclude: + - sdk/network/pom.xml + - sdk/network/azure-resourcemanager-network/pom.xml + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/network/ + exclude: + - sdk/network/pom.xml + - sdk/network/azure-resourcemanager-network/pom.xml + +parameters: +- name: release_azureresourcemanagernetwork + displayName: 'azure-resourcemanager-network' + type: boolean + default: false + +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: network + Artifacts: + - name: azure-resourcemanager-network + groupId: com.azure.resourcemanager + safeName: azureresourcemanagernetwork + releaseInBatch: `${{ parameters.release_azureresourcemanagernetwork }} +"@ + + # Sample POM with profiles (as in the root pom) + $script:SamplePomWithProfiles = @" + + + + default + + sdk/advisor + sdk/storage + + + + other + + sdk/other + + + + +"@ +} + +AfterAll { + # Clean up test directory + if (Test-Path $script:TestRoot) { + Remove-Item -Path $script:TestRoot -Recurse -Force -ErrorAction SilentlyContinue + } +} + +# ============================================================================ +# Get-ServiceAndModuleFromPath tests +# ============================================================================ +Describe "Get-ServiceAndModuleFromPath" { + It "Should extract service and module from Windows-style path" { + $result = Get-ServiceAndModuleFromPath ` + -PackagePath "C:\repos\azure-sdk-for-java\sdk\network\azure-resourcemanager-network" ` + -SdkRepoPath "C:\repos\azure-sdk-for-java" + + $result.Service | Should -Be "network" + $result.Module | Should -Be "azure-resourcemanager-network" + } + + It "Should extract service and module from forward-slash path" { + $result = Get-ServiceAndModuleFromPath ` + -PackagePath "C:/repos/azure-sdk-for-java/sdk/storage/azure-storage-blob" ` + -SdkRepoPath "C:/repos/azure-sdk-for-java" + + $result.Service | Should -Be "storage" + $result.Module | Should -Be "azure-storage-blob" + } + + It "Should handle trailing slashes" { + $result = Get-ServiceAndModuleFromPath ` + -PackagePath "C:\repos\azure-sdk-for-java\sdk\network\azure-resourcemanager-network\" ` + -SdkRepoPath "C:\repos\azure-sdk-for-java\" + + $result.Service | Should -Be "network" + $result.Module | Should -Be "azure-resourcemanager-network" + } + + It "Should throw when path is not under sdk/ directory" { + { Get-ServiceAndModuleFromPath ` + -PackagePath "C:\repos\other-repo\lib\something" ` + -SdkRepoPath "C:\repos\azure-sdk-for-java" } | Should -Throw "*not under sdk/ directory*" + } + + It "Should throw when path has insufficient parts" { + { Get-ServiceAndModuleFromPath ` + -PackagePath "C:\repos\azure-sdk-for-java\sdk\network" ` + -SdkRepoPath "C:\repos\azure-sdk-for-java" } | Should -Throw "*Cannot determine service and module*" + } +} + +# ============================================================================ +# Get-GroupIdFromPom tests +# ============================================================================ +Describe "Get-GroupIdFromPom" { + BeforeEach { + $script:TestPomPath = Join-Path $script:TestRoot "pom.xml" + } + + AfterEach { + if (Test-Path $script:TestPomPath) { + Remove-Item $script:TestPomPath -Force + } + } + + It "Should extract direct groupId" { + Set-Content -Path $script:TestPomPath -Value $script:SamplePomXml + $result = Get-GroupIdFromPom -PomPath $script:TestPomPath + $result | Should -Be "com.azure.resourcemanager" + } + + It "Should extract groupId from parent when not in project" { + Set-Content -Path $script:TestPomPath -Value $script:SamplePomWithParentXml + $result = Get-GroupIdFromPom -PomPath $script:TestPomPath + $result | Should -Be "com.azure" + } + + It "Should throw when groupId is missing entirely" { + $invalidPom = @" + + + 4.0.0 + some-artifact + +"@ + Set-Content -Path $script:TestPomPath -Value $invalidPom + { Get-GroupIdFromPom -PomPath $script:TestPomPath } | Should -Throw "*Could not extract groupId*" + } +} + +# ============================================================================ +# Add-ModuleToModulesBlock tests +# ============================================================================ +Describe "Add-ModuleToModulesBlock" { + It "Should add a new module in sorted order" { + $block = @" + + sdk/advisor + sdk/storage + +"@ + $result = Add-ModuleToModulesBlock -ModulesBlock $block -Module "sdk/network" + $result | Should -Match "sdk/advisor" + $result | Should -Match "sdk/network" + $result | Should -Match "sdk/storage" + + # Verify order: advisor < network < storage + $advisorIdx = $result.IndexOf("sdk/advisor") + $networkIdx = $result.IndexOf("sdk/network") + $storageIdx = $result.IndexOf("sdk/storage") + $networkIdx | Should -BeGreaterThan $advisorIdx + $storageIdx | Should -BeGreaterThan $networkIdx + } + + It "Should not duplicate an existing module" { + $block = @" + + sdk/network + sdk/storage + +"@ + $result = Add-ModuleToModulesBlock -ModulesBlock $block -Module "sdk/network" + $count = ([regex]::Matches($result, 'sdk/network')).Count + $count | Should -Be 1 + } + + It "Should preserve indentation from the source block" { + $block = " `n sdk/a`n " + $result = Add-ModuleToModulesBlock -ModulesBlock $block -Module "sdk/b" + # The closing had indent " " (4 spaces), so module indent should be " " (6 spaces) + $result | Should -Match " sdk/b" + } +} + +# ============================================================================ +# Add-ModuleToPom tests +# ============================================================================ +Describe "Add-ModuleToPom" { + It "Should add module to a simple POM" { + $pom = @" + + + azure-resourcemanager-network + + +"@ + $result = Add-ModuleToPom -PomContent $pom -Module "azure-resourcemanager-compute" + $result.Success | Should -Be $true + $result.Content | Should -Match "azure-resourcemanager-compute" + $result.Content | Should -Match "azure-resourcemanager-network" + } + + It "Should skip when module already exists (idempotent)" { + $pom = @" + + + azure-resourcemanager-network + + +"@ + $result = Add-ModuleToPom -PomContent $pom -Module "azure-resourcemanager-network" + $result.Success | Should -Be $true + $result.Content | Should -Be $pom + } + + It "Should add module to root POM style (sdk/ prefixed)" { + $result = Add-ModuleToPom -PomContent $script:SampleRootPomXml -Module "sdk/newservice" + $result.Success | Should -Be $true + $result.Content | Should -Match "sdk/newservice" + + # Verify sorted order + $networkIdx = $result.Content.IndexOf("sdk/network") + $newserviceIdx = $result.Content.IndexOf("sdk/newservice") + $storageIdx = $result.Content.IndexOf("sdk/storage") + $newserviceIdx | Should -BeGreaterThan $networkIdx + $storageIdx | Should -BeGreaterThan $newserviceIdx + } + + It "Should fail when no block exists" { + $pom = "com.azure" + $result = Add-ModuleToPom -PomContent $pom -Module "new-module" + $result.Success | Should -Be $false + } + + It "Should fail when multiple blocks without profiles" { + $pom = @" + + + a + + + b + + +"@ + $result = Add-ModuleToPom -PomContent $pom -Module "c" + $result.Success | Should -Be $false + } +} + +# ============================================================================ +# Add-ModuleToDefaultProfile tests +# ============================================================================ +Describe "Add-ModuleToDefaultProfile" { + It "Should add module to the default profile's modules" { + $result = Add-ModuleToDefaultProfile -PomContent $script:SamplePomWithProfiles -Module "sdk/network" + $result.Success | Should -Be $true + $result.Content | Should -Match "sdk/network" + + # Verify it was added to the default profile, not the other + # Check order in default profile: advisor < network < storage + $defaultProfileMatch = [regex]::Match($result.Content, '(?s)default.*?') + $defaultProfile = $defaultProfileMatch.Value + $defaultProfile | Should -Match "sdk/advisor" + $defaultProfile | Should -Match "sdk/network" + $defaultProfile | Should -Match "sdk/storage" + } + + It "Should not modify other profiles" { + $result = Add-ModuleToDefaultProfile -PomContent $script:SamplePomWithProfiles -Module "sdk/network" + $result.Success | Should -Be $true + + # The "other" profile should still only have sdk/other + $otherProfileMatch = [regex]::Match($result.Content, '(?s)other.*?') + $otherProfile = $otherProfileMatch.Value + $otherProfile | Should -Not -Match "sdk/network" + $otherProfile | Should -Match "sdk/other" + } + + It "Should fail when no default profile exists" { + $pom = @" + + + + custom + + sdk/a + + + + +"@ + $result = Add-ModuleToDefaultProfile -PomContent $pom -Module "sdk/b" + $result.Success | Should -Be $false + } +} + +# ============================================================================ +# Add-ModuleToPom with profiles tests +# ============================================================================ +Describe "Add-ModuleToPom with profiles" { + It "Should delegate to default profile when multiple and exist" { + $result = Add-ModuleToPom -PomContent $script:SamplePomWithProfiles -Module "sdk/network" + $result.Success | Should -Be $true + $result.Content | Should -Match "sdk/network" + } +} + +# ============================================================================ +# Update-RootPom tests +# ============================================================================ +Describe "Update-RootPom" { + BeforeEach { + $script:TestSdkRoot = Join-Path $script:TestRoot "sdk-root-$(New-Guid)" + New-Item -ItemType Directory -Path $script:TestSdkRoot -Force | Out-Null + } + + AfterEach { + if (Test-Path $script:TestSdkRoot) { + Remove-Item -Path $script:TestSdkRoot -Recurse -Force + } + } + + It "Should add new service module to root pom" { + Set-Content -Path (Join-Path $script:TestSdkRoot "pom.xml") -Value $script:SampleRootPomXml + + Update-RootPom -SdkRepoPath $script:TestSdkRoot -Service "compute" + + $content = Get-Content (Join-Path $script:TestSdkRoot "pom.xml") -Raw + $content | Should -Match "sdk/compute" + } + + It "Should skip when service already exists" { + Set-Content -Path (Join-Path $script:TestSdkRoot "pom.xml") -Value $script:SampleRootPomXml + + Update-RootPom -SdkRepoPath $script:TestSdkRoot -Service "network" + + $content = Get-Content (Join-Path $script:TestSdkRoot "pom.xml") -Raw + $count = ([regex]::Matches($content, 'sdk/network')).Count + $count | Should -Be 1 + } + + It "Should not throw when root pom does not exist" { + { Update-RootPom -SdkRepoPath $script:TestSdkRoot -Service "newservice" } | Should -Not -Throw + } +} + +# ============================================================================ +# Update-ServicePom tests +# ============================================================================ +Describe "Update-ServicePom" { + BeforeEach { + $script:TestSdkRoot = Join-Path $script:TestRoot "sdk-svc-$(New-Guid)" + New-Item -ItemType Directory -Path (Join-Path $script:TestSdkRoot "sdk" "network") -Force | Out-Null + } + + AfterEach { + if (Test-Path $script:TestSdkRoot) { + Remove-Item -Path $script:TestSdkRoot -Recurse -Force + } + } + + It "Should create new service pom when it does not exist" { + Update-ServicePom -SdkRepoPath $script:TestSdkRoot -Service "newservice" -Module "azure-resourcemanager-newservice" + + $pomFile = Join-Path $script:TestSdkRoot "sdk" "newservice" "pom.xml" + Test-Path $pomFile | Should -Be $true + + $content = Get-Content $pomFile -Raw + $content | Should -Match "azure-newservice-service" + $content | Should -Match "azure-resourcemanager-newservice" + } + + It "Should add module to existing service pom" { + $existingPom = Join-Path $script:TestSdkRoot "sdk" "network" "pom.xml" + Set-Content -Path $existingPom -Value $script:SampleServicePomXml + + Update-ServicePom -SdkRepoPath $script:TestSdkRoot -Service "network" -Module "azure-resourcemanager-network-extra" + + $content = Get-Content $existingPom -Raw + $content | Should -Match "azure-resourcemanager-network" + $content | Should -Match "azure-resourcemanager-network-extra" + } + + It "Should skip when module already exists in service pom" { + $existingPom = Join-Path $script:TestSdkRoot "sdk" "network" "pom.xml" + Set-Content -Path $existingPom -Value $script:SampleServicePomXml + + Update-ServicePom -SdkRepoPath $script:TestSdkRoot -Service "network" -Module "azure-resourcemanager-network" + + $content = Get-Content $existingPom -Raw + $count = ([regex]::Matches($content, 'azure-resourcemanager-network')).Count + $count | Should -Be 1 + } +} + +# ============================================================================ +# Update-CiYml tests +# ============================================================================ +Describe "Update-CiYml" { + BeforeEach { + $script:TestSdkRoot = Join-Path $script:TestRoot "sdk-ci-$(New-Guid)" + New-Item -ItemType Directory -Path (Join-Path $script:TestSdkRoot "sdk" "network") -Force | Out-Null + } + + AfterEach { + if (Test-Path $script:TestSdkRoot) { + Remove-Item -Path $script:TestSdkRoot -Recurse -Force + } + } + + It "Should skip ci.yml creation when file does not exist (new service)" { + Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "newservice" ` + -Module "azure-resourcemanager-newservice" -GroupId "com.azure.resourcemanager" + + $ciFile = Join-Path $script:TestSdkRoot "sdk" "newservice" "ci.yml" + Test-Path $ciFile | Should -Be $false + } + + It "Should add artifact to existing ci.yml for existing service" { + $ciFile = Join-Path $script:TestSdkRoot "sdk" "network" "ci.yml" + Set-Content -Path $ciFile -Value $script:SampleCiYml + + Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "network" ` + -Module "azure-resourcemanager-network-extra" -GroupId "com.azure.resourcemanager" + + $content = Get-Content $ciFile -Raw + $content | Should -Match "name: azure-resourcemanager-network-extra" + $content | Should -Match "groupId: com.azure.resourcemanager" + $content | Should -Match "safeName: azureresourcemanagernetworkextra" + $content | Should -Match "release_azureresourcemanagernetworkextra" + } + + It "Should skip when artifact already exists in ci.yml" { + $ciFile = Join-Path $script:TestSdkRoot "sdk" "network" "ci.yml" + Set-Content -Path $ciFile -Value $script:SampleCiYml + + Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "network" ` + -Module "azure-resourcemanager-network" -GroupId "com.azure.resourcemanager" + + $content = Get-Content $ciFile -Raw + # Should have exactly one artifact entry for azure-resourcemanager-network + $count = ([regex]::Matches($content, 'name: azure-resourcemanager-network\s')).Count + $count | Should -Be 1 + } + + It "Should not rename data-plane ci.yml when SDKType is data (handled separately)" { + $ciFile = Join-Path $script:TestSdkRoot "sdk" "network" "ci.yml" + $dataCi = @" +trigger: + branches: + include: + - main + +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + SDKType: data + ServiceDirectory: network + Artifacts: [] +"@ + Set-Content -Path $ciFile -Value $dataCi + + Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "network" ` + -Module "azure-resourcemanager-network" -GroupId "com.azure.resourcemanager" + + # ci.yml should still exist as-is with the new artifact appended + $content = Get-Content $ciFile -Raw + $content | Should -Match "name: azure-resourcemanager-network" + + # ci.data.yml should NOT have been created (rename is not this script's job) + $dataFile = Join-Path $script:TestSdkRoot "sdk" "network" "ci.data.yml" + Test-Path $dataFile | Should -Be $false + } + + It "Should add artifact with correct format to existing ci.yml" { + # Set up existing ci.yml with one artifact + $existingCi = @" +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - hotfix/* + - release/* + paths: + include: + - sdk/testservice/ci.yml + - sdk/testservice/azure-messaging-testservice/ + exclude: + - sdk/testservice/pom.xml + - sdk/testservice/azure-messaging-testservice/pom.xml + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/testservice/ci.yml + - sdk/testservice/azure-messaging-testservice/ + exclude: + - sdk/testservice/pom.xml + - sdk/testservice/azure-messaging-testservice/pom.xml + +parameters: +- name: release_azuremessagingtestservice + displayName: 'azure-messaging-testservice' + type: boolean + default: true + +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: testservice + Artifacts: + - name: azure-messaging-testservice + groupId: com.azure + safeName: azuremessagingtestservice + releaseInBatch: `${{ parameters.release_azuremessagingtestservice }} +"@ + $testSvcDir = Join-Path $script:TestSdkRoot "sdk" "testservice" + New-Item -ItemType Directory -Path $testSvcDir -Force | Out-Null + $ciFile = Join-Path $testSvcDir "ci.yml" + Set-Content -Path $ciFile -Value $existingCi + + Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "testservice" ` + -Module "azure-resourcemanager-testservice" -GroupId "com.azure.resourcemanager" + + $content = Get-Content $ciFile -Raw + + # Verify new artifact added + $content | Should -Match "name: azure-resourcemanager-testservice" + $content | Should -Match "groupId: com.azure.resourcemanager" + $content | Should -Match "safeName: azureresourcemanagertestservice" + $content | Should -Match "release_azureresourcemanagertestservice" + $content | Should -Match "displayName: 'azure-resourcemanager-testservice'" + $content | Should -Match "default: false" + + # Verify existing artifact still present + $content | Should -Match "name: azure-messaging-testservice" + + # Verify paths were added + $content | Should -Match "sdk/testservice/azure-resourcemanager-testservice/" + $content | Should -Match "sdk/testservice/azure-resourcemanager-testservice/pom.xml" + } +} + +# ============================================================================ +# Add-PathToCiYml tests +# ============================================================================ +Describe "Add-PathToCiYml" { + It "Should add include and exclude paths for a new module" { + $ci = @" +trigger: + paths: + include: + - sdk/svc/existing-module/ + exclude: + - sdk/svc/existing-module/pom.xml + +pr: + paths: + include: + - sdk/svc/existing-module/ + exclude: + - sdk/svc/existing-module/pom.xml + +parameters: +"@ + $result = Add-PathToCiYml -CiContent $ci -Service "svc" -Module "new-module" + $result | Should -Match "sdk/svc/new-module/" + $result | Should -Match "sdk/svc/new-module/pom.xml" + } + + It "Should not add duplicate paths when module already present" { + $ci = @" +trigger: + paths: + include: + - sdk/svc/my-module/ + exclude: + - sdk/svc/my-module/pom.xml + +pr: + paths: + include: + - sdk/svc/my-module/ + exclude: + - sdk/svc/my-module/pom.xml + +parameters: +"@ + $result = Add-PathToCiYml -CiContent $ci -Service "svc" -Module "my-module" + $includeCount = ([regex]::Matches($result, 'sdk/svc/my-module/')).Count + # Each occurrence in include + exclude for trigger + pr = 4 + $includeCount | Should -BeLessOrEqual 4 + } +} + +# ============================================================================ +# Write-NewCiYmlFile tests +# ============================================================================ +Describe "Write-NewCiYmlFile" { + BeforeEach { + $script:TestCiDir = Join-Path $script:TestRoot "ci-test-$(New-Guid)" + New-Item -ItemType Directory -Path $script:TestCiDir -Force | Out-Null + } + + AfterEach { + if (Test-Path $script:TestCiDir) { + Remove-Item -Path $script:TestCiDir -Recurse -Force + } + } + + It "Should create a properly formatted ci.yml" { + $ciFile = Join-Path $script:TestCiDir "ci.yml" + Write-NewCiYmlFile -CiFile $ciFile -Service "myservice" -Module "azure-resourcemanager-myservice" ` + -GroupId "com.azure.resourcemanager" -SafeName "azureresourcemanagermyservice" ` + -ReleaseParameterName "release_azureresourcemanagermyservice" -ReleaseDefault "false" + + Test-Path $ciFile | Should -Be $true + $content = Get-Content $ciFile -Raw + $content | Should -Match "ServiceDirectory: myservice" + $content | Should -Match "name: azure-resourcemanager-myservice" + $content | Should -Match "groupId: com.azure.resourcemanager" + } + + It "Should create parent directory if needed" { + $ciFile = Join-Path $script:TestCiDir "subdir" "ci.yml" + Write-NewCiYmlFile -CiFile $ciFile -Service "s" -Module "m" -GroupId "g" ` + -SafeName "m" -ReleaseParameterName "release_m" -ReleaseDefault "true" + + Test-Path $ciFile | Should -Be $true + } +} + +# ============================================================================ +# Script integration tests +# ============================================================================ +Describe "Script Integration" { + It "Should verify the main script imports the helper correctly" { + $scriptPath = Join-Path $PSScriptRoot ".." "Automation-Sdk-UpdateMetadata.ps1" + $scriptContent = Get-Content $scriptPath -Raw + + $scriptContent | Should -Match '\. \$helperPath' + $scriptContent | Should -Match 'Metadata-Helpers\.ps1' + } + + It "Should verify the main script does not duplicate function definitions" { + $scriptPath = Join-Path $PSScriptRoot ".." "Automation-Sdk-UpdateMetadata.ps1" + $scriptContent = Get-Content $scriptPath -Raw + + $scriptContent | Should -Not -Match 'function Get-ServiceAndModuleFromPath' + $scriptContent | Should -Not -Match 'function Get-GroupIdFromPom' + $scriptContent | Should -Not -Match 'function Add-ModuleToPom' + $scriptContent | Should -Not -Match 'function Update-RootPom' + $scriptContent | Should -Not -Match 'function Update-ServicePom' + $scriptContent | Should -Not -Match 'function Update-CiYml' + } + + It "Should verify the helper file exports all required functions" { + $helperPath = Join-Path $PSScriptRoot ".." "helpers" "Metadata-Helpers.ps1" + + { . $helperPath } | Should -Not -Throw + + Get-Command Get-ServiceAndModuleFromPath -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Get-GroupIdFromPom -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Add-ModuleToModulesBlock -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Add-ModuleToPom -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Add-ModuleToDefaultProfile -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Update-RootPom -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Update-ServicePom -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Update-CiYml -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Write-NewCiYmlFile -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Add-PathToCiYml -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + } + + It "Should verify swagger_to_sdk_config.json references the metadata script" { + $configPath = Join-Path $PSScriptRoot ".." ".." "swagger_to_sdk_config.json" + $config = Get-Content $configPath -Raw | ConvertFrom-Json + + $config.packageOptions.updateMetadataScript.path | Should -Be "./eng/scripts/Automation-Sdk-UpdateMetadata.ps1" + } +} + +# ============================================================================ +# End-to-end: Case 1 - Existing service, new resourcemanager package +# ============================================================================ +Describe "End-to-End: Existing Service, New Package" { + BeforeEach { + $script:E2ERoot = Join-Path $script:TestRoot "e2e-case1-$(New-Guid)" + New-Item -ItemType Directory -Path $script:E2ERoot -Force | Out-Null + + # Set up SDK repo with existing service (already in root pom, has service pom and ci.yml) + Set-Content -Path (Join-Path $script:E2ERoot "pom.xml") -Value $script:SampleRootPomXml + $svcDir = Join-Path $script:E2ERoot "sdk" "network" + New-Item -ItemType Directory -Path $svcDir -Force | Out-Null + Set-Content -Path (Join-Path $svcDir "pom.xml") -Value $script:SampleServicePomXml + Set-Content -Path (Join-Path $svcDir "ci.yml") -Value $script:SampleCiYml + + # Package directory exists + $pkgDir = Join-Path $svcDir "azure-resourcemanager-network-extra" + New-Item -ItemType Directory -Path $pkgDir -Force | Out-Null + Set-Content -Path (Join-Path $pkgDir "pom.xml") -Value $script:SamplePomXml + } + + AfterEach { + if (Test-Path $script:E2ERoot) { + Remove-Item -Path $script:E2ERoot -Recurse -Force + } + } + + It "Should update service pom and ci.yml but skip root pom" { + $service = "network" + $module = "azure-resourcemanager-network-extra" + $groupId = "com.azure.resourcemanager" + + $rootPomBefore = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw + + Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service + Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module + Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId + + # Root pom unchanged (service already existed) + $rootPomAfter = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw + $rootPomAfter | Should -Be $rootPomBefore + + # Service pom has the new module + $svcPomContent = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "pom.xml") -Raw + $svcPomContent | Should -Match "azure-resourcemanager-network" + $svcPomContent | Should -Match "azure-resourcemanager-network-extra" + + # ci.yml has the new artifact + $ciContent = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "ci.yml") -Raw + $ciContent | Should -Match "name: azure-resourcemanager-network-extra" + $ciContent | Should -Match "groupId: com.azure.resourcemanager" + } + + It "Should be idempotent when run twice" { + $service = "network" + $module = "azure-resourcemanager-network-extra" + $groupId = "com.azure.resourcemanager" + + # First run + Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service + Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module + Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId + + $rootPom1 = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw + $svcPom1 = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "pom.xml") -Raw + $ci1 = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "ci.yml") -Raw + + # Second run + Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service + Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module + Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId + + $rootPom2 = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw + $svcPom2 = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "pom.xml") -Raw + $ci2 = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "ci.yml") -Raw + + $rootPom2 | Should -Be $rootPom1 + $svcPom2 | Should -Be $svcPom1 + $ci2 | Should -Be $ci1 + } +} + +# ============================================================================ +# End-to-end: Case 2 - Brand new service +# ============================================================================ +Describe "End-to-End: New Service" { + BeforeEach { + $script:E2ERoot = Join-Path $script:TestRoot "e2e-case2-$(New-Guid)" + New-Item -ItemType Directory -Path $script:E2ERoot -Force | Out-Null + + # Set up minimal SDK repo structure (root pom only, no service dir) + Set-Content -Path (Join-Path $script:E2ERoot "pom.xml") -Value $script:SampleRootPomXml + $pkgDir = Join-Path $script:E2ERoot "sdk" "compute" "azure-resourcemanager-compute" + New-Item -ItemType Directory -Path $pkgDir -Force | Out-Null + Set-Content -Path (Join-Path $pkgDir "pom.xml") -Value $script:SamplePomXml + } + + AfterEach { + if (Test-Path $script:E2ERoot) { + Remove-Item -Path $script:E2ERoot -Recurse -Force + } + } + + It "Should update root pom and create service pom, but skip ci.yml" { + $service = "compute" + $module = "azure-resourcemanager-compute" + $groupId = "com.azure.resourcemanager" + + Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service + Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module + Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId + + # Verify root pom has new service + $rootPom = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw + $rootPom | Should -Match "sdk/compute" + + # Verify service pom was created + $svcPom = Join-Path $script:E2ERoot "sdk" "compute" "pom.xml" + Test-Path $svcPom | Should -Be $true + $svcPomContent = Get-Content $svcPom -Raw + $svcPomContent | Should -Match "azure-resourcemanager-compute" + + # Verify ci.yml was NOT created (handled by a separate script) + $ciFile = Join-Path $script:E2ERoot "sdk" "compute" "ci.yml" + Test-Path $ciFile | Should -Be $false + } + + It "Should be idempotent when run twice" { + $service = "compute" + $module = "azure-resourcemanager-compute" + $groupId = "com.azure.resourcemanager" + + # First run + Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service + Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module + Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId + + $rootPom1 = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw + $svcPom1 = Get-Content (Join-Path $script:E2ERoot "sdk" "compute" "pom.xml") -Raw + + # Second run + Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service + Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module + Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId + + $rootPom2 = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw + $svcPom2 = Get-Content (Join-Path $script:E2ERoot "sdk" "compute" "pom.xml") -Raw + + $rootPom2 | Should -Be $rootPom1 + $svcPom2 | Should -Be $svcPom1 + + # ci.yml should still not exist + $ciFile = Join-Path $script:E2ERoot "sdk" "compute" "ci.yml" + Test-Path $ciFile | Should -Be $false + } +} diff --git a/eng/swagger_to_sdk_config.json b/eng/swagger_to_sdk_config.json index 505259f7bf6f..346af09f88be 100644 --- a/eng/swagger_to_sdk_config.json +++ b/eng/swagger_to_sdk_config.json @@ -31,6 +31,9 @@ }, "updateChangelogContentScript": { "path": "./eng/scripts/Automation-Sdk-UpdateChangelog.ps1" + }, + "updateMetadataScript": { + "path": "./eng/scripts/Automation-Sdk-UpdateMetadata.ps1" } } } From f643baac262aa0a0fbeafefdc0ee5ed77323747d Mon Sep 17 00:00:00 2001 From: Haoling Dong Date: Mon, 2 Mar 2026 13:31:21 +0800 Subject: [PATCH 2/5] remove ci.yaml updates --- eng/scripts/Automation-Sdk-UpdateMetadata.ps1 | 18 +- eng/scripts/helpers/Metadata-Helpers.ps1 | 284 --------------- .../Automation-Sdk-UpdateMetadata.tests.ps1 | 331 +----------------- 3 files changed, 6 insertions(+), 627 deletions(-) diff --git a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 index 4f0f610fe70d..5a02df6c9039 100644 --- a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 +++ b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 @@ -3,18 +3,16 @@ <# .SYNOPSIS - Updates parent-level and root-level pom.xml files and ci.yml for a Java SDK package. + Updates parent-level and root-level pom.xml files for a Java SDK package. .DESCRIPTION This script handles two cases: Case 1 - Service exists, new resourcemanager package: - Skips root pom (service already listed) - Updates service-level pom.xml with new module - - Updates existing ci.yml with new artifact entry Case 2 - Brand new service: - Updates root pom.xml with new service module - Creates service-level pom.xml from template - - Skips ci.yml (creation handled by a separate script) .PARAMETER PackagePath Absolute path to the root folder of the local SDK project (containing pom.xml). @@ -60,24 +58,14 @@ try { LogInfo " Module: $module" LogInfo "" - LogInfo "Step 2: Reading groupId from package POM..." - $pomPath = Join-Path $PackagePath "pom.xml" - $groupId = Get-GroupIdFromPom -PomPath $pomPath - LogInfo " Group ID: $groupId" - LogInfo "" - - LogInfo "Step 3: Updating root pom.xml..." + LogInfo "Step 2: Updating root pom.xml..." Update-RootPom -SdkRepoPath $SdkRepoPath -Service $service LogInfo "" - LogInfo "Step 4: Updating service-level pom.xml..." + LogInfo "Step 3: Updating service-level pom.xml..." Update-ServicePom -SdkRepoPath $SdkRepoPath -Service $service -Module $module LogInfo "" - LogInfo "Step 5: Updating ci.yml..." - Update-CiYml -SdkRepoPath $SdkRepoPath -Service $service -Module $module -GroupId $groupId - LogInfo "" - LogInfo "✅ Metadata updated successfully!" exit 0 } diff --git a/eng/scripts/helpers/Metadata-Helpers.ps1 b/eng/scripts/helpers/Metadata-Helpers.ps1 index 2a33f07a2fe5..623313bbd6a8 100644 --- a/eng/scripts/helpers/Metadata-Helpers.ps1 +++ b/eng/scripts/helpers/Metadata-Helpers.ps1 @@ -387,287 +387,3 @@ function Update-ServicePom { } } } - -# Equivalent of the CI update part of Python update_service_files_for_new_lib() -function Update-CiYml { - <# - .SYNOPSIS - Adds an artifact entry to the service ci.yml file, creating it if it doesn't exist. - - .DESCRIPTION - Follows the same logic as Python update_service_files_for_new_lib() for ci.yml: - - If ci.yml doesn't exist, create from template - - If ci.yml exists with SDKType=data, rename to ci.data.yml and create new ci.yml - - If artifact already exists, skip - - Otherwise add the artifact (with release parameter) - Uses text-based operations to preserve the existing YAML format. - - .PARAMETER SdkRepoPath - Absolute path to the SDK repository root. - - .PARAMETER Service - The service directory name. - - .PARAMETER Module - The artifact/module name. - - .PARAMETER GroupId - The Maven groupId for the artifact. - #> - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [string]$SdkRepoPath, - - [Parameter(Mandatory = $true)] - [string]$Service, - - [Parameter(Mandatory = $true)] - [string]$Module, - - [Parameter(Mandatory = $true)] - [string]$GroupId - ) - - $ciFile = Join-Path $SdkRepoPath "sdk" $Service "ci.yml" - $safeName = $Module -replace '-', '' - $isMgmt = $Module -match '-resourcemanager-' - $releaseDefault = if ($isMgmt) { "false" } else { "true" } - $releaseParameterName = "release_$safeName" - - if (-not (Test-Path $ciFile)) { - # ci.yml creation for new services is handled by a separate script - if (Get-Command LogInfo -ErrorAction SilentlyContinue) { - LogInfo "[CI][Skip] ci.yml does not exist, skipping (new service ci.yml creation is handled separately)" - } - return - } - - # Read existing ci.yml - $existingContent = Get-Content -Path $ciFile -Raw - - # Check if artifact already exists - if ($existingContent -match "name:\s+$([regex]::Escape($Module))\s") { - if (Get-Command LogInfo -ErrorAction SilentlyContinue) { - LogInfo "[CI][Skip] ci.yml already has artifact $Module" - } - return - } - - if (Get-Command LogInfo -ErrorAction SilentlyContinue) { - LogInfo "[CI][Process] updating ci.yml with artifact $Module" - } - - # Add path entries to trigger and pr includes/excludes - $updatedContent = Add-PathToCiYml -CiContent $existingContent -Service $Service -Module $Module - - # Add release parameter before "extends:" line - $paramBlock = "- name: ${releaseParameterName}`n displayName: '${Module}'`n type: boolean`n default: ${releaseDefault}`n`n" - - $extendsIdx = $updatedContent.IndexOf("`nextends:") - if ($extendsIdx -ge 0) { - $extendsIdx += 1 # skip the newline - $updatedContent = $updatedContent.Substring(0, $extendsIdx) + $paramBlock + $updatedContent.Substring($extendsIdx) - } - elseif ($updatedContent.IndexOf("extends:") -ge 0) { - $extendsIdx = $updatedContent.IndexOf("extends:") - $updatedContent = $updatedContent.Substring(0, $extendsIdx) + $paramBlock + $updatedContent.Substring($extendsIdx) - } - - # Add artifact entry at end of file - $artifactBlock = " - name: ${Module}`n groupId: ${GroupId}`n safeName: ${SafeName}`n releaseInBatch: `${{ parameters.${releaseParameterName} }}" - $updatedContent = $updatedContent.TrimEnd() + "`n" + $artifactBlock + "`n" - - Set-Content -Path $ciFile -Value $updatedContent -NoNewline - if (Get-Command LogInfo -ErrorAction SilentlyContinue) { - LogInfo "[CI][Success] Updated ci.yml with artifact $Module" - } -} - -function Write-NewCiYmlFile { - <# - .SYNOPSIS - Creates a brand-new ci.yml file from scratch. - #> - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)][string]$CiFile, - [Parameter(Mandatory = $true)][string]$Service, - [Parameter(Mandatory = $true)][string]$Module, - [Parameter(Mandatory = $true)][string]$GroupId, - [Parameter(Mandatory = $true)][string]$SafeName, - [Parameter(Mandatory = $true)][string]$ReleaseParameterName, - [Parameter(Mandatory = $true)][string]$ReleaseDefault - ) - - $content = @" -# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. - -trigger: - branches: - include: - - main - - hotfix/* - - release/* - paths: - include: - - sdk/${Service}/ci.yml - - sdk/${Service}/${Module}/ - exclude: - - sdk/${Service}/pom.xml - - sdk/${Service}/${Module}/pom.xml - -pr: - branches: - include: - - main - - feature/* - - hotfix/* - - release/* - paths: - include: - - sdk/${Service}/ci.yml - - sdk/${Service}/${Module}/ - exclude: - - sdk/${Service}/pom.xml - - sdk/${Service}/${Module}/pom.xml - -parameters: -- name: ${ReleaseParameterName} - displayName: '${Module}' - type: boolean - default: ${ReleaseDefault} - -extends: - template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml - parameters: - ServiceDirectory: ${Service} - Artifacts: - - name: ${Module} - groupId: ${GroupId} - safeName: ${SafeName} - releaseInBatch: `${{ parameters.${ReleaseParameterName} }} -"@ - - $ciDir = Split-Path $CiFile -Parent - if (-not (Test-Path $ciDir)) { - New-Item -ItemType Directory -Path $ciDir -Force | Out-Null - } - Set-Content -Path $CiFile -Value $content - if (Get-Command LogInfo -ErrorAction SilentlyContinue) { - LogInfo "[CI][Success] Created new ci.yml at: $CiFile" - } -} - -function Add-PathToCiYml { - <# - .SYNOPSIS - Adds include/exclude paths for a module to both trigger and pr sections. - - .PARAMETER CiContent - The ci.yml content as a string. - - .PARAMETER Service - The service directory name. - - .PARAMETER Module - The module name. - - .OUTPUTS - Updated ci.yml content string. - #> - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [string]$CiContent, - - [Parameter(Mandatory = $true)] - [string]$Service, - - [Parameter(Mandatory = $true)] - [string]$Module - ) - - $includeEntry = " - sdk/${Service}/${Module}/" - $excludeEntry = " - sdk/${Service}/${Module}/pom.xml" - $modulePathEscaped = [regex]::Escape("sdk/${Service}/${Module}/") - - $lines = $CiContent -split "`n" - $newLines = [System.Collections.Generic.List[string]]::new() - - $inSection = "" # "trigger" or "pr" - $inPaths = $false - $inInclude = $false - $inExclude = $false - $addedInclude = $false - $addedExclude = $false - $hasModuleInclude = $CiContent -match $modulePathEscaped - - for ($i = 0; $i -lt $lines.Count; $i++) { - $line = $lines[$i] - $trimmed = $line.Trim() - - # Track which top-level section we're in - if ($line -match '^trigger:') { $inSection = "trigger"; $addedInclude = $false; $addedExclude = $false } - elseif ($line -match '^pr:') { $inSection = "pr"; $addedInclude = $false; $addedExclude = $false } - elseif ($line -match '^[a-zA-Z]') { - if ($trimmed -ne 'trigger:' -and $trimmed -ne 'pr:') { - $inSection = "" - $inPaths = $false - $inInclude = $false - $inExclude = $false - } - } - - if ($inSection -ne "" -and $trimmed -eq 'paths:') { $inPaths = $true } - if ($inPaths -and $trimmed -eq 'include:') { $inInclude = $true; $inExclude = $false } - if ($inPaths -and $trimmed -eq 'exclude:') { $inExclude = $true; $inInclude = $false } - - # Detect end of include block - if ($inInclude -and -not $addedInclude -and $trimmed -match '^-') { - $nextIdx = $i + 1 - if ($nextIdx -lt $lines.Count) { - $nextTrimmed = $lines[$nextIdx].Trim() - if ($nextTrimmed -eq 'exclude:' -or ($nextTrimmed -ne '' -and $nextTrimmed -notmatch '^-')) { - if (-not $hasModuleInclude) { - $newLines.Add($line) - $newLines.Add($includeEntry) - $addedInclude = $true - continue - } - } - } - } - - # Detect end of exclude block - if ($inExclude -and -not $addedExclude -and $trimmed -match '^-') { - $nextIdx = $i + 1 - $isLastExcludeLine = $false - if ($nextIdx -lt $lines.Count) { - $nextTrimmed = $lines[$nextIdx].Trim() - if ($nextTrimmed -eq '' -or ($nextTrimmed -notmatch '^-')) { - $isLastExcludeLine = $true - } - } - else { - $isLastExcludeLine = $true - } - - if ($isLastExcludeLine) { - $moduleExcludeEscaped = [regex]::Escape("sdk/${Service}/${Module}/pom.xml") - if ($CiContent -notmatch $moduleExcludeEscaped) { - $newLines.Add($line) - $newLines.Add($excludeEntry) - $addedExclude = $true - $inExclude = $false - $inPaths = $false - continue - } - } - } - - $newLines.Add($line) - } - - return $newLines -join "`n" -} diff --git a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 index 1ce70ceb3e2a..ed49367d0675 100644 --- a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 +++ b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 @@ -83,54 +83,6 @@ BeforeAll { "@ - # Sample ci.yml (single-artifact management library) - $script:SampleCiYml = @" -# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. - -trigger: - branches: - include: - - main - - hotfix/* - - release/* - paths: - include: - - sdk/network/ - exclude: - - sdk/network/pom.xml - - sdk/network/azure-resourcemanager-network/pom.xml - -pr: - branches: - include: - - main - - feature/* - - hotfix/* - - release/* - paths: - include: - - sdk/network/ - exclude: - - sdk/network/pom.xml - - sdk/network/azure-resourcemanager-network/pom.xml - -parameters: -- name: release_azureresourcemanagernetwork - displayName: 'azure-resourcemanager-network' - type: boolean - default: false - -extends: - template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml - parameters: - ServiceDirectory: network - Artifacts: - - name: azure-resourcemanager-network - groupId: com.azure.resourcemanager - safeName: azureresourcemanagernetwork - releaseInBatch: `${{ parameters.release_azureresourcemanagernetwork }} -"@ - # Sample POM with profiles (as in the root pom) $script:SamplePomWithProfiles = @" @@ -499,251 +451,6 @@ Describe "Update-ServicePom" { } } -# ============================================================================ -# Update-CiYml tests -# ============================================================================ -Describe "Update-CiYml" { - BeforeEach { - $script:TestSdkRoot = Join-Path $script:TestRoot "sdk-ci-$(New-Guid)" - New-Item -ItemType Directory -Path (Join-Path $script:TestSdkRoot "sdk" "network") -Force | Out-Null - } - - AfterEach { - if (Test-Path $script:TestSdkRoot) { - Remove-Item -Path $script:TestSdkRoot -Recurse -Force - } - } - - It "Should skip ci.yml creation when file does not exist (new service)" { - Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "newservice" ` - -Module "azure-resourcemanager-newservice" -GroupId "com.azure.resourcemanager" - - $ciFile = Join-Path $script:TestSdkRoot "sdk" "newservice" "ci.yml" - Test-Path $ciFile | Should -Be $false - } - - It "Should add artifact to existing ci.yml for existing service" { - $ciFile = Join-Path $script:TestSdkRoot "sdk" "network" "ci.yml" - Set-Content -Path $ciFile -Value $script:SampleCiYml - - Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "network" ` - -Module "azure-resourcemanager-network-extra" -GroupId "com.azure.resourcemanager" - - $content = Get-Content $ciFile -Raw - $content | Should -Match "name: azure-resourcemanager-network-extra" - $content | Should -Match "groupId: com.azure.resourcemanager" - $content | Should -Match "safeName: azureresourcemanagernetworkextra" - $content | Should -Match "release_azureresourcemanagernetworkextra" - } - - It "Should skip when artifact already exists in ci.yml" { - $ciFile = Join-Path $script:TestSdkRoot "sdk" "network" "ci.yml" - Set-Content -Path $ciFile -Value $script:SampleCiYml - - Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "network" ` - -Module "azure-resourcemanager-network" -GroupId "com.azure.resourcemanager" - - $content = Get-Content $ciFile -Raw - # Should have exactly one artifact entry for azure-resourcemanager-network - $count = ([regex]::Matches($content, 'name: azure-resourcemanager-network\s')).Count - $count | Should -Be 1 - } - - It "Should not rename data-plane ci.yml when SDKType is data (handled separately)" { - $ciFile = Join-Path $script:TestSdkRoot "sdk" "network" "ci.yml" - $dataCi = @" -trigger: - branches: - include: - - main - -extends: - template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml - parameters: - SDKType: data - ServiceDirectory: network - Artifacts: [] -"@ - Set-Content -Path $ciFile -Value $dataCi - - Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "network" ` - -Module "azure-resourcemanager-network" -GroupId "com.azure.resourcemanager" - - # ci.yml should still exist as-is with the new artifact appended - $content = Get-Content $ciFile -Raw - $content | Should -Match "name: azure-resourcemanager-network" - - # ci.data.yml should NOT have been created (rename is not this script's job) - $dataFile = Join-Path $script:TestSdkRoot "sdk" "network" "ci.data.yml" - Test-Path $dataFile | Should -Be $false - } - - It "Should add artifact with correct format to existing ci.yml" { - # Set up existing ci.yml with one artifact - $existingCi = @" -# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. - -trigger: - branches: - include: - - main - - hotfix/* - - release/* - paths: - include: - - sdk/testservice/ci.yml - - sdk/testservice/azure-messaging-testservice/ - exclude: - - sdk/testservice/pom.xml - - sdk/testservice/azure-messaging-testservice/pom.xml - -pr: - branches: - include: - - main - - feature/* - - hotfix/* - - release/* - paths: - include: - - sdk/testservice/ci.yml - - sdk/testservice/azure-messaging-testservice/ - exclude: - - sdk/testservice/pom.xml - - sdk/testservice/azure-messaging-testservice/pom.xml - -parameters: -- name: release_azuremessagingtestservice - displayName: 'azure-messaging-testservice' - type: boolean - default: true - -extends: - template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml - parameters: - ServiceDirectory: testservice - Artifacts: - - name: azure-messaging-testservice - groupId: com.azure - safeName: azuremessagingtestservice - releaseInBatch: `${{ parameters.release_azuremessagingtestservice }} -"@ - $testSvcDir = Join-Path $script:TestSdkRoot "sdk" "testservice" - New-Item -ItemType Directory -Path $testSvcDir -Force | Out-Null - $ciFile = Join-Path $testSvcDir "ci.yml" - Set-Content -Path $ciFile -Value $existingCi - - Update-CiYml -SdkRepoPath $script:TestSdkRoot -Service "testservice" ` - -Module "azure-resourcemanager-testservice" -GroupId "com.azure.resourcemanager" - - $content = Get-Content $ciFile -Raw - - # Verify new artifact added - $content | Should -Match "name: azure-resourcemanager-testservice" - $content | Should -Match "groupId: com.azure.resourcemanager" - $content | Should -Match "safeName: azureresourcemanagertestservice" - $content | Should -Match "release_azureresourcemanagertestservice" - $content | Should -Match "displayName: 'azure-resourcemanager-testservice'" - $content | Should -Match "default: false" - - # Verify existing artifact still present - $content | Should -Match "name: azure-messaging-testservice" - - # Verify paths were added - $content | Should -Match "sdk/testservice/azure-resourcemanager-testservice/" - $content | Should -Match "sdk/testservice/azure-resourcemanager-testservice/pom.xml" - } -} - -# ============================================================================ -# Add-PathToCiYml tests -# ============================================================================ -Describe "Add-PathToCiYml" { - It "Should add include and exclude paths for a new module" { - $ci = @" -trigger: - paths: - include: - - sdk/svc/existing-module/ - exclude: - - sdk/svc/existing-module/pom.xml - -pr: - paths: - include: - - sdk/svc/existing-module/ - exclude: - - sdk/svc/existing-module/pom.xml - -parameters: -"@ - $result = Add-PathToCiYml -CiContent $ci -Service "svc" -Module "new-module" - $result | Should -Match "sdk/svc/new-module/" - $result | Should -Match "sdk/svc/new-module/pom.xml" - } - - It "Should not add duplicate paths when module already present" { - $ci = @" -trigger: - paths: - include: - - sdk/svc/my-module/ - exclude: - - sdk/svc/my-module/pom.xml - -pr: - paths: - include: - - sdk/svc/my-module/ - exclude: - - sdk/svc/my-module/pom.xml - -parameters: -"@ - $result = Add-PathToCiYml -CiContent $ci -Service "svc" -Module "my-module" - $includeCount = ([regex]::Matches($result, 'sdk/svc/my-module/')).Count - # Each occurrence in include + exclude for trigger + pr = 4 - $includeCount | Should -BeLessOrEqual 4 - } -} - -# ============================================================================ -# Write-NewCiYmlFile tests -# ============================================================================ -Describe "Write-NewCiYmlFile" { - BeforeEach { - $script:TestCiDir = Join-Path $script:TestRoot "ci-test-$(New-Guid)" - New-Item -ItemType Directory -Path $script:TestCiDir -Force | Out-Null - } - - AfterEach { - if (Test-Path $script:TestCiDir) { - Remove-Item -Path $script:TestCiDir -Recurse -Force - } - } - - It "Should create a properly formatted ci.yml" { - $ciFile = Join-Path $script:TestCiDir "ci.yml" - Write-NewCiYmlFile -CiFile $ciFile -Service "myservice" -Module "azure-resourcemanager-myservice" ` - -GroupId "com.azure.resourcemanager" -SafeName "azureresourcemanagermyservice" ` - -ReleaseParameterName "release_azureresourcemanagermyservice" -ReleaseDefault "false" - - Test-Path $ciFile | Should -Be $true - $content = Get-Content $ciFile -Raw - $content | Should -Match "ServiceDirectory: myservice" - $content | Should -Match "name: azure-resourcemanager-myservice" - $content | Should -Match "groupId: com.azure.resourcemanager" - } - - It "Should create parent directory if needed" { - $ciFile = Join-Path $script:TestCiDir "subdir" "ci.yml" - Write-NewCiYmlFile -CiFile $ciFile -Service "s" -Module "m" -GroupId "g" ` - -SafeName "m" -ReleaseParameterName "release_m" -ReleaseDefault "true" - - Test-Path $ciFile | Should -Be $true - } -} - # ============================================================================ # Script integration tests # ============================================================================ @@ -761,11 +468,9 @@ Describe "Script Integration" { $scriptContent = Get-Content $scriptPath -Raw $scriptContent | Should -Not -Match 'function Get-ServiceAndModuleFromPath' - $scriptContent | Should -Not -Match 'function Get-GroupIdFromPom' $scriptContent | Should -Not -Match 'function Add-ModuleToPom' $scriptContent | Should -Not -Match 'function Update-RootPom' $scriptContent | Should -Not -Match 'function Update-ServicePom' - $scriptContent | Should -Not -Match 'function Update-CiYml' } It "Should verify the helper file exports all required functions" { @@ -780,9 +485,6 @@ Describe "Script Integration" { Get-Command Add-ModuleToDefaultProfile -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty Get-Command Update-RootPom -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty Get-Command Update-ServicePom -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty - Get-Command Update-CiYml -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty - Get-Command Write-NewCiYmlFile -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty - Get-Command Add-PathToCiYml -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty } It "Should verify swagger_to_sdk_config.json references the metadata script" { @@ -801,12 +503,11 @@ Describe "End-to-End: Existing Service, New Package" { $script:E2ERoot = Join-Path $script:TestRoot "e2e-case1-$(New-Guid)" New-Item -ItemType Directory -Path $script:E2ERoot -Force | Out-Null - # Set up SDK repo with existing service (already in root pom, has service pom and ci.yml) + # Set up SDK repo with existing service (already in root pom, has service pom) Set-Content -Path (Join-Path $script:E2ERoot "pom.xml") -Value $script:SampleRootPomXml $svcDir = Join-Path $script:E2ERoot "sdk" "network" New-Item -ItemType Directory -Path $svcDir -Force | Out-Null Set-Content -Path (Join-Path $svcDir "pom.xml") -Value $script:SampleServicePomXml - Set-Content -Path (Join-Path $svcDir "ci.yml") -Value $script:SampleCiYml # Package directory exists $pkgDir = Join-Path $svcDir "azure-resourcemanager-network-extra" @@ -820,16 +521,14 @@ Describe "End-to-End: Existing Service, New Package" { } } - It "Should update service pom and ci.yml but skip root pom" { + It "Should update service pom but skip root pom" { $service = "network" $module = "azure-resourcemanager-network-extra" - $groupId = "com.azure.resourcemanager" $rootPomBefore = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module - Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId # Root pom unchanged (service already existed) $rootPomAfter = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw @@ -839,39 +538,28 @@ Describe "End-to-End: Existing Service, New Package" { $svcPomContent = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "pom.xml") -Raw $svcPomContent | Should -Match "azure-resourcemanager-network" $svcPomContent | Should -Match "azure-resourcemanager-network-extra" - - # ci.yml has the new artifact - $ciContent = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "ci.yml") -Raw - $ciContent | Should -Match "name: azure-resourcemanager-network-extra" - $ciContent | Should -Match "groupId: com.azure.resourcemanager" } It "Should be idempotent when run twice" { $service = "network" $module = "azure-resourcemanager-network-extra" - $groupId = "com.azure.resourcemanager" # First run Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module - Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId $rootPom1 = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw $svcPom1 = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "pom.xml") -Raw - $ci1 = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "ci.yml") -Raw # Second run Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module - Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId $rootPom2 = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw $svcPom2 = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "pom.xml") -Raw - $ci2 = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "ci.yml") -Raw $rootPom2 | Should -Be $rootPom1 $svcPom2 | Should -Be $svcPom1 - $ci2 | Should -Be $ci1 } } @@ -896,14 +584,12 @@ Describe "End-to-End: New Service" { } } - It "Should update root pom and create service pom, but skip ci.yml" { + It "Should update root pom and create service pom" { $service = "compute" $module = "azure-resourcemanager-compute" - $groupId = "com.azure.resourcemanager" Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module - Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId # Verify root pom has new service $rootPom = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw @@ -914,21 +600,15 @@ Describe "End-to-End: New Service" { Test-Path $svcPom | Should -Be $true $svcPomContent = Get-Content $svcPom -Raw $svcPomContent | Should -Match "azure-resourcemanager-compute" - - # Verify ci.yml was NOT created (handled by a separate script) - $ciFile = Join-Path $script:E2ERoot "sdk" "compute" "ci.yml" - Test-Path $ciFile | Should -Be $false } It "Should be idempotent when run twice" { $service = "compute" $module = "azure-resourcemanager-compute" - $groupId = "com.azure.resourcemanager" # First run Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module - Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId $rootPom1 = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw $svcPom1 = Get-Content (Join-Path $script:E2ERoot "sdk" "compute" "pom.xml") -Raw @@ -936,16 +616,11 @@ Describe "End-to-End: New Service" { # Second run Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module - Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId $rootPom2 = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw $svcPom2 = Get-Content (Join-Path $script:E2ERoot "sdk" "compute" "pom.xml") -Raw $rootPom2 | Should -Be $rootPom1 $svcPom2 | Should -Be $svcPom1 - - # ci.yml should still not exist - $ciFile = Join-Path $script:E2ERoot "sdk" "compute" "ci.yml" - Test-Path $ciFile | Should -Be $false } } From 847679a828a4c4b08ebff6cdbe332fbfd73aaf77 Mon Sep 17 00:00:00 2001 From: Haoling Dong Date: Fri, 20 Mar 2026 15:56:48 +0800 Subject: [PATCH 3/5] Add CI.yml provisioning and update support Adds CI.yml update as Step 4 in the metadata update script, ported from the Python implementation in eng/automation/utils.py. Supported cases: - Create new ci.yml from template for brand-new services - Add artifact entry to existing ci.yml (with/without release params) - Rename ci.yml to ci.data.yml when SDKType=data and create new ci.yml - Skip when module already exists (idempotent) - Management-plane packages get default: false for release param - Data-plane packages get default: true for release param Fixes https://github.com/Azure/azure-sdk-tools/issues/13808 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/scripts/Automation-Sdk-UpdateMetadata.ps1 | 16 +- eng/scripts/helpers/Metadata-Helpers.ps1 | 300 ++++++++++++- .../Automation-Sdk-UpdateMetadata.tests.ps1 | 395 ++++++++++++++++++ 3 files changed, 709 insertions(+), 2 deletions(-) diff --git a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 index 5a02df6c9039..f99ff6fe2842 100644 --- a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 +++ b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 @@ -3,16 +3,18 @@ <# .SYNOPSIS - Updates parent-level and root-level pom.xml files for a Java SDK package. + Updates parent-level and root-level pom.xml files, and ci.yml for a Java SDK package. .DESCRIPTION This script handles two cases: Case 1 - Service exists, new resourcemanager package: - Skips root pom (service already listed) - Updates service-level pom.xml with new module + - Updates ci.yml with new artifact entry Case 2 - Brand new service: - Updates root pom.xml with new service module - Creates service-level pom.xml from template + - Creates ci.yml from template with new artifact entry .PARAMETER PackagePath Absolute path to the root folder of the local SDK project (containing pom.xml). @@ -66,6 +68,18 @@ try { Update-ServicePom -SdkRepoPath $SdkRepoPath -Service $service -Module $module LogInfo "" + LogInfo "Step 4: Updating ci.yml..." + $packagePomPath = Join-Path $PackagePath "pom.xml" + if (Test-Path $packagePomPath) { + $groupId = Get-GroupIdFromPom -PomPath $packagePomPath + LogInfo " GroupId: $groupId" + Update-CiYml -SdkRepoPath $SdkRepoPath -Service $service -Module $module -GroupId $groupId + } + else { + LogInfo "[CI][Skip] No pom.xml found at package path, skipping ci.yml update" + } + LogInfo "" + LogInfo "✅ Metadata updated successfully!" exit 0 } diff --git a/eng/scripts/helpers/Metadata-Helpers.ps1 b/eng/scripts/helpers/Metadata-Helpers.ps1 index 623313bbd6a8..8aa5b0c1bd93 100644 --- a/eng/scripts/helpers/Metadata-Helpers.ps1 +++ b/eng/scripts/helpers/Metadata-Helpers.ps1 @@ -8,7 +8,7 @@ .DESCRIPTION This file provides helper functions for updating parent-level and root-level pom.xml files, as well as ci.yml files for Java SDK packages. - Logic is ported from eng/automation/utils.py (Python). + Logic is ported from eng/automation/utils.py and parameters.py (Python). #> function Get-ServiceAndModuleFromPath { @@ -387,3 +387,301 @@ function Update-ServicePom { } } } + +# ============================================================================ +# CI.yml update functions +# Ported from eng/automation/utils.py update_service_files_for_new_lib() and +# eng/automation/parameters.py CI_FORMAT / CI_HEADER +# ============================================================================ + +function New-CiYmlContent { + <# + .SYNOPSIS + Generates a new ci.yml content string from template. + + .PARAMETER Service + The service directory name (e.g., "network"). + + .PARAMETER Module + The artifact/module name (e.g., "azure-resourcemanager-network"). + + .OUTPUTS + String representing the ci.yml content. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$Service, + + [Parameter(Mandatory = $true)] + [string]$Module + ) + + # Matches Python CI_HEADER + CI_FORMAT from parameters.py + return @" +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - hotfix/* + - release/* + paths: + include: + - sdk/${Service}/ci.yml + - sdk/${Service}/${Module}/ + exclude: + - sdk/${Service}/pom.xml + - sdk/${Service}/${Module}/pom.xml + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/${Service}/ci.yml + - sdk/${Service}/${Module}/ + exclude: + - sdk/${Service}/pom.xml + - sdk/${Service}/${Module}/pom.xml + +parameters: [] + +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: ${Service} + Artifacts: [] +"@ +} + +function Add-ArtifactToCiYml { + <# + .SYNOPSIS + Adds an artifact entry to a parsed ci.yml object. + + .DESCRIPTION + Handles two modes based on whether the ci.yml has a parameters list: + - With parameters: adds artifact with releaseInBatch and a release parameter + - Without parameters: adds artifact without releaseInBatch + + .PARAMETER CiYml + The parsed ci.yml as a hashtable/ordered dictionary (from ConvertFrom-Yaml). + + .PARAMETER Module + The artifact/module name. + + .PARAMETER GroupId + The Maven groupId. + + .OUTPUTS + Boolean indicating whether the artifact was added. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $CiYml, + + [Parameter(Mandatory = $true)] + [string]$Module, + + [Parameter(Mandatory = $true)] + [string]$GroupId + ) + + # Validate structure + $extends = $CiYml["extends"] + if (-not ($extends -is [System.Collections.IDictionary])) { + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[CI][Skip] Unexpected ci.yml format: missing 'extends'" + } + return $false + } + $params = $extends["parameters"] + if (-not ($params -is [System.Collections.IDictionary])) { + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[CI][Skip] Unexpected ci.yml format: missing 'extends.parameters'" + } + return $false + } + $artifacts = $params["Artifacts"] + if (-not ($artifacts -is [System.Collections.IList])) { + if (Get-Command LogError -ErrorAction SilentlyContinue) { + LogError "[CI][Skip] Unexpected ci.yml format: 'Artifacts' is not a list" + } + return $false + } + + # Check if module already exists + foreach ($artifact in $artifacts) { + if ($artifact["name"] -eq $Module -and $artifact["groupId"] -eq $GroupId) { + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Skip] ci.yml already has module $Module" + } + return $false + } + } + + $safeName = $Module.Replace("-", "") + + # Check if ci.yml has a parameters list (not just empty/null) + $ciParameters = $CiYml["parameters"] + $hasParametersList = ($ciParameters -is [System.Collections.IList]) -and ($ciParameters.Count -gt 0) + + if ($hasParametersList) { + # Add artifact with releaseInBatch reference + $releaseParameterName = "release_$safeName" + $releaseInBatchRef = "`${{ parameters.$releaseParameterName }}" + + $newArtifact = [ordered]@{ + name = $Module + groupId = $GroupId + safeName = $safeName + releaseInBatch = $releaseInBatchRef + } + $artifacts.Add($newArtifact) + + # True for data-plane, False for management-plane + $releaseInBatchDefault = -not ($Module -match "-resourcemanager-") + + $newParam = [ordered]@{ + name = $releaseParameterName + displayName = $Module + type = "boolean" + default = $releaseInBatchDefault + } + $ciParameters.Add($newParam) + } + else { + # Add artifact without releaseInBatch + $newArtifact = [ordered]@{ + name = $Module + groupId = $GroupId + safeName = $safeName + } + $artifacts.Add($newArtifact) + } + + return $true +} + +function ConvertTo-CiYmlString { + <# + .SYNOPSIS + Converts a ci.yml object back to a YAML string with proper formatting. + + .DESCRIPTION + Uses powershell-yaml to serialize, then applies formatting fixes to match + the expected ci.yml style (blank lines between top-level sections). + + .PARAMETER CiYml + The ci.yml object to serialize. + + .OUTPUTS + String representing the formatted YAML content. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $CiYml + ) + + $yamlStr = ConvertTo-Yaml $CiYml + + # Add blank line before each top-level key (matches Python: re.sub(r"(\n\S)", r"\n\1", ci_yml_str)) + $yamlStr = $yamlStr -replace '(\n)(\S)', "`$1`n`$2" + + # Add CI header + $header = "# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file.`n`n" + + return $header + $yamlStr +} + +function Update-CiYml { + <# + .SYNOPSIS + Updates or creates the ci.yml file for a service. + + .DESCRIPTION + Ported from Python update_service_files_for_new_lib() CI logic. + Cases: + 1. ci.yml doesn't exist → create from template, add artifact + 2. ci.yml exists with SDKType=data → rename to ci.data.yml, create new, add artifact + 3. ci.yml exists, module already present → skip + 4. ci.yml exists, module not present → add artifact (with or without release param) + + .PARAMETER SdkRepoPath + Absolute path to the SDK repository root. + + .PARAMETER Service + The service directory name. + + .PARAMETER Module + The artifact/module name. + + .PARAMETER GroupId + The Maven groupId for the artifact. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$SdkRepoPath, + + [Parameter(Mandatory = $true)] + [string]$Service, + + [Parameter(Mandatory = $true)] + [string]$Module, + + [Parameter(Mandatory = $true)] + [string]$GroupId + ) + + $ciYmlFile = Join-Path $SdkRepoPath "sdk" $Service "ci.yml" + + if (Test-Path $ciYmlFile) { + $ciYmlContent = Get-Content -Path $ciYmlFile -Raw + $ciYml = ConvertFrom-Yaml $ciYmlContent -Ordered + + # Check for SDKType=data → rename and create new + $sdkType = "" + try { + $sdkType = $ciYml["extends"]["parameters"]["SDKType"] + } catch {} + + if ($sdkType -is [string] -and $sdkType.ToLower() -eq "data") { + $ciDataFile = Join-Path $SdkRepoPath "sdk" $Service "ci.data.yml" + Move-Item -Path $ciYmlFile -Destination $ciDataFile -Force + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Process] Renamed existing ci.yml (SDKType=data) to ci.data.yml" + } + $ciYmlContent = New-CiYmlContent -Service $Service -Module $Module + $ciYml = ConvertFrom-Yaml $ciYmlContent -Ordered + } + } + else { + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Process] Creating new ci.yml for service: $Service" + } + $ciYmlContent = New-CiYmlContent -Service $Service -Module $Module + $ciYml = ConvertFrom-Yaml $ciYmlContent -Ordered + } + + $added = Add-ArtifactToCiYml -CiYml $ciYml -Module $Module -GroupId $GroupId + if ($added) { + $outputStr = ConvertTo-CiYmlString -CiYml $ciYml + $ciDir = Split-Path $ciYmlFile -Parent + if (-not (Test-Path $ciDir)) { + New-Item -ItemType Directory -Path $ciDir -Force | Out-Null + } + Set-Content -Path $ciYmlFile -Value $outputStr -NoNewline + if (Get-Command LogInfo -ErrorAction SilentlyContinue) { + LogInfo "[CI][Success] Write to ci.yml" + } + } +} diff --git a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 index ed49367d0675..87a516e425ee 100644 --- a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 +++ b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 @@ -19,6 +19,9 @@ #> BeforeAll { + # Import YAML module for CI tests + Import-Module powershell-yaml -ErrorAction Stop + # Import metadata helper functions $helperPath = Join-Path $PSScriptRoot ".." "helpers" "Metadata-Helpers.ps1" . $helperPath @@ -102,6 +105,113 @@ BeforeAll { +"@ + + # Sample ci.yml with no parameters (simple artifact) + $script:SampleCiYmlNoParams = @" +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - hotfix/* + - release/* + paths: + include: + - sdk/communication/azure-resourcemanager-communication/ + exclude: + - sdk/communication/azure-resourcemanager-communication/pom.xml + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/communication/azure-resourcemanager-communication/ + exclude: + - sdk/communication/azure-resourcemanager-communication/pom.xml + +extends: + template: /eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: communication/azure-resourcemanager-communication + Artifacts: + - name: azure-resourcemanager-communication + groupId: com.azure.resourcemanager + safeName: azureresourcemanagercommunication +"@ + + # Sample ci.yml with release parameters + $script:SampleCiYmlWithParams = @" +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - hotfix/* + - release/* + paths: + include: + - sdk/network/ + exclude: + - sdk/network/pom.xml + - sdk/network/azure-resourcemanager-network/pom.xml + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/network/ + exclude: + - sdk/network/pom.xml + - sdk/network/azure-resourcemanager-network/pom.xml + +parameters: +- name: release_azureresourcemanagernetwork + displayName: 'azure-resourcemanager-network' + type: boolean + default: false + +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: network + Artifacts: + - name: azure-resourcemanager-network + groupId: com.azure.resourcemanager + safeName: azureresourcemanagernetwork + releaseInBatch: `${{ parameters.release_azureresourcemanagernetwork }} +"@ + + # Sample ci.yml with SDKType=data + $script:SampleCiYmlDataType = @" +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + +extends: + template: ../../eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: myservice + SDKType: data + Artifacts: + - name: azure-myservice + groupId: com.azure + safeName: azuremyservice "@ } @@ -485,6 +595,10 @@ Describe "Script Integration" { Get-Command Add-ModuleToDefaultProfile -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty Get-Command Update-RootPom -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty Get-Command Update-ServicePom -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command New-CiYmlContent -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Add-ArtifactToCiYml -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command ConvertTo-CiYmlString -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty + Get-Command Update-CiYml -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty } It "Should verify swagger_to_sdk_config.json references the metadata script" { @@ -624,3 +738,284 @@ Describe "End-to-End: New Service" { $svcPom2 | Should -Be $svcPom1 } } + +# ============================================================================ +# New-CiYmlContent tests +# ============================================================================ +Describe "New-CiYmlContent" { + It "Should generate valid YAML with correct service and module" { + $result = New-CiYmlContent -Service "network" -Module "azure-resourcemanager-network" + + $result | Should -Match "sdk/network/ci.yml" + $result | Should -Match "sdk/network/azure-resourcemanager-network/" + $result | Should -Match "sdk/network/azure-resourcemanager-network/pom.xml" + $result | Should -Match "ServiceDirectory: network" + $result | Should -Match "Artifacts: \[\]" + } + + It "Should be parseable YAML" { + $content = New-CiYmlContent -Service "compute" -Module "azure-resourcemanager-compute" + $parsed = ConvertFrom-Yaml $content -Ordered + $parsed["trigger"] | Should -Not -BeNullOrEmpty + $parsed["pr"] | Should -Not -BeNullOrEmpty + $parsed["extends"]["parameters"]["ServiceDirectory"] | Should -Be "compute" + } +} + +# ============================================================================ +# Add-ArtifactToCiYml tests +# ============================================================================ +Describe "Add-ArtifactToCiYml" { + It "Should add artifact without releaseInBatch when no parameters exist" { + $content = New-CiYmlContent -Service "newservice" -Module "azure-resourcemanager-newservice" + $ciYml = ConvertFrom-Yaml $content -Ordered + + $result = Add-ArtifactToCiYml -CiYml $ciYml -Module "azure-resourcemanager-newservice" -GroupId "com.azure.resourcemanager" + + $result | Should -Be $true + $artifacts = $ciYml["extends"]["parameters"]["Artifacts"] + $artifacts.Count | Should -Be 1 + $artifacts[0]["name"] | Should -Be "azure-resourcemanager-newservice" + $artifacts[0]["groupId"] | Should -Be "com.azure.resourcemanager" + $artifacts[0]["safeName"] | Should -Be "azureresourcemanagernewservice" + $artifacts[0].Keys | Should -Not -Contain "releaseInBatch" + } + + It "Should add artifact with releaseInBatch when parameters list exists" { + $ciYml = ConvertFrom-Yaml $script:SampleCiYmlWithParams -Ordered + + $result = Add-ArtifactToCiYml -CiYml $ciYml -Module "azure-resourcemanager-network-extra" -GroupId "com.azure.resourcemanager" + + $result | Should -Be $true + $artifacts = $ciYml["extends"]["parameters"]["Artifacts"] + $artifacts.Count | Should -Be 2 + $newArtifact = $artifacts[1] + $newArtifact["name"] | Should -Be "azure-resourcemanager-network-extra" + $newArtifact["releaseInBatch"] | Should -Not -BeNullOrEmpty + + # Check release parameter was added + $params = $ciYml["parameters"] + $params.Count | Should -Be 2 + $newParam = $params[1] + $newParam["name"] | Should -Be "release_azureresourcemanagernetworkextra" + $newParam["default"] | Should -Be $false # management-plane + } + + It "Should set release param default=true for data-plane packages" { + $ciYml = ConvertFrom-Yaml $script:SampleCiYmlWithParams -Ordered + + $result = Add-ArtifactToCiYml -CiYml $ciYml -Module "azure-storage-blob" -GroupId "com.azure" + + $result | Should -Be $true + $params = $ciYml["parameters"] + $newParam = $params | Where-Object { $_["name"] -eq "release_azurestorageblob" } + $newParam["default"] | Should -Be $true # data-plane + } + + It "Should skip when module already exists" { + $ciYml = ConvertFrom-Yaml $script:SampleCiYmlWithParams -Ordered + + $result = Add-ArtifactToCiYml -CiYml $ciYml -Module "azure-resourcemanager-network" -GroupId "com.azure.resourcemanager" + + $result | Should -Be $false + $ciYml["extends"]["parameters"]["Artifacts"].Count | Should -Be 1 + } + + It "Should return false for unexpected format" { + $ciYml = [ordered]@{ "trigger" = @{} } + + $result = Add-ArtifactToCiYml -CiYml $ciYml -Module "azure-test" -GroupId "com.azure" + + $result | Should -Be $false + } +} + +# ============================================================================ +# Update-CiYml tests +# ============================================================================ +Describe "Update-CiYml" { + BeforeEach { + $script:CiTestRoot = Join-Path $script:TestRoot "ci-test-$(New-Guid)" + New-Item -ItemType Directory -Path $script:CiTestRoot -Force | Out-Null + } + + AfterEach { + if (Test-Path $script:CiTestRoot) { + Remove-Item -Path $script:CiTestRoot -Recurse -Force + } + } + + It "Should create ci.yml when it does not exist" { + $svcDir = Join-Path $script:CiTestRoot "sdk" "newservice" + New-Item -ItemType Directory -Path $svcDir -Force | Out-Null + + Update-CiYml -SdkRepoPath $script:CiTestRoot -Service "newservice" -Module "azure-resourcemanager-newservice" -GroupId "com.azure.resourcemanager" + + $ciFile = Join-Path $svcDir "ci.yml" + Test-Path $ciFile | Should -Be $true + + $content = Get-Content $ciFile -Raw + $content | Should -Match "azure-resourcemanager-newservice" + $content | Should -Match "com.azure.resourcemanager" + $content | Should -Match "azureresourcemanagernewservice" + } + + It "Should add artifact to existing ci.yml without parameters" { + $svcDir = Join-Path $script:CiTestRoot "sdk" "communication" + New-Item -ItemType Directory -Path $svcDir -Force | Out-Null + Set-Content -Path (Join-Path $svcDir "ci.yml") -Value $script:SampleCiYmlNoParams + + Update-CiYml -SdkRepoPath $script:CiTestRoot -Service "communication" -Module "azure-resourcemanager-communication-extra" -GroupId "com.azure.resourcemanager" + + $content = Get-Content (Join-Path $svcDir "ci.yml") -Raw + $content | Should -Match "azure-resourcemanager-communication" + $content | Should -Match "azure-resourcemanager-communication-extra" + } + + It "Should add artifact to existing ci.yml with release parameters" { + $svcDir = Join-Path $script:CiTestRoot "sdk" "network" + New-Item -ItemType Directory -Path $svcDir -Force | Out-Null + Set-Content -Path (Join-Path $svcDir "ci.yml") -Value $script:SampleCiYmlWithParams + + Update-CiYml -SdkRepoPath $script:CiTestRoot -Service "network" -Module "azure-resourcemanager-network-extra" -GroupId "com.azure.resourcemanager" + + $content = Get-Content (Join-Path $svcDir "ci.yml") -Raw + $content | Should -Match "azure-resourcemanager-network-extra" + $content | Should -Match "release_azureresourcemanagernetworkextra" + } + + It "Should skip when module already exists in ci.yml" { + $svcDir = Join-Path $script:CiTestRoot "sdk" "network" + New-Item -ItemType Directory -Path $svcDir -Force | Out-Null + Set-Content -Path (Join-Path $svcDir "ci.yml") -Value $script:SampleCiYmlWithParams + + $before = Get-Content (Join-Path $svcDir "ci.yml") -Raw + + Update-CiYml -SdkRepoPath $script:CiTestRoot -Service "network" -Module "azure-resourcemanager-network" -GroupId "com.azure.resourcemanager" + + $after = Get-Content (Join-Path $svcDir "ci.yml") -Raw + $after | Should -Be $before + } + + It "Should rename ci.yml to ci.data.yml when SDKType=data and create new ci.yml" { + $svcDir = Join-Path $script:CiTestRoot "sdk" "myservice" + New-Item -ItemType Directory -Path $svcDir -Force | Out-Null + Set-Content -Path (Join-Path $svcDir "ci.yml") -Value $script:SampleCiYmlDataType + + Update-CiYml -SdkRepoPath $script:CiTestRoot -Service "myservice" -Module "azure-resourcemanager-myservice" -GroupId "com.azure.resourcemanager" + + # ci.data.yml should exist + Test-Path (Join-Path $svcDir "ci.data.yml") | Should -Be $true + + # New ci.yml should have the new module + $content = Get-Content (Join-Path $svcDir "ci.yml") -Raw + $content | Should -Match "azure-resourcemanager-myservice" + $content | Should -Not -Match "SDKType" + } + + It "Should be idempotent when run twice" { + $svcDir = Join-Path $script:CiTestRoot "sdk" "network" + New-Item -ItemType Directory -Path $svcDir -Force | Out-Null + Set-Content -Path (Join-Path $svcDir "ci.yml") -Value $script:SampleCiYmlWithParams + + Update-CiYml -SdkRepoPath $script:CiTestRoot -Service "network" -Module "azure-resourcemanager-network-extra" -GroupId "com.azure.resourcemanager" + $content1 = Get-Content (Join-Path $svcDir "ci.yml") -Raw + + Update-CiYml -SdkRepoPath $script:CiTestRoot -Service "network" -Module "azure-resourcemanager-network-extra" -GroupId "com.azure.resourcemanager" + $content2 = Get-Content (Join-Path $svcDir "ci.yml") -Raw + + $content2 | Should -Be $content1 + } +} + +# ============================================================================ +# End-to-end: CI.yml with POM - Existing service, new package +# ============================================================================ +Describe "End-to-End: Existing Service with CI update" { + BeforeEach { + $script:E2ERoot = Join-Path $script:TestRoot "e2e-ci-case1-$(New-Guid)" + New-Item -ItemType Directory -Path $script:E2ERoot -Force | Out-Null + + Set-Content -Path (Join-Path $script:E2ERoot "pom.xml") -Value $script:SampleRootPomXml + $svcDir = Join-Path $script:E2ERoot "sdk" "network" + New-Item -ItemType Directory -Path $svcDir -Force | Out-Null + Set-Content -Path (Join-Path $svcDir "pom.xml") -Value $script:SampleServicePomXml + Set-Content -Path (Join-Path $svcDir "ci.yml") -Value $script:SampleCiYmlWithParams + + $pkgDir = Join-Path $svcDir "azure-resourcemanager-network-extra" + New-Item -ItemType Directory -Path $pkgDir -Force | Out-Null + Set-Content -Path (Join-Path $pkgDir "pom.xml") -Value $script:SamplePomXml + } + + AfterEach { + if (Test-Path $script:E2ERoot) { + Remove-Item -Path $script:E2ERoot -Recurse -Force + } + } + + It "Should update service pom, ci.yml, and skip root pom" { + $service = "network" + $module = "azure-resourcemanager-network-extra" + $groupId = "com.azure.resourcemanager" + + Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service + Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module + Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId + + # Service pom has the new module + $svcPomContent = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "pom.xml") -Raw + $svcPomContent | Should -Match "azure-resourcemanager-network-extra" + + # ci.yml has the new artifact + $ciContent = Get-Content (Join-Path $script:E2ERoot "sdk" "network" "ci.yml") -Raw + $ciContent | Should -Match "azure-resourcemanager-network-extra" + $ciContent | Should -Match "release_azureresourcemanagernetworkextra" + } +} + +# ============================================================================ +# End-to-end: CI.yml with POM - Brand new service +# ============================================================================ +Describe "End-to-End: New Service with CI update" { + BeforeEach { + $script:E2ERoot = Join-Path $script:TestRoot "e2e-ci-case2-$(New-Guid)" + New-Item -ItemType Directory -Path $script:E2ERoot -Force | Out-Null + + Set-Content -Path (Join-Path $script:E2ERoot "pom.xml") -Value $script:SampleRootPomXml + $pkgDir = Join-Path $script:E2ERoot "sdk" "compute" "azure-resourcemanager-compute" + New-Item -ItemType Directory -Path $pkgDir -Force | Out-Null + Set-Content -Path (Join-Path $pkgDir "pom.xml") -Value $script:SamplePomXml + } + + AfterEach { + if (Test-Path $script:E2ERoot) { + Remove-Item -Path $script:E2ERoot -Recurse -Force + } + } + + It "Should create ci.yml, service pom, and update root pom" { + $service = "compute" + $module = "azure-resourcemanager-compute" + $groupId = "com.azure.resourcemanager" + + Update-RootPom -SdkRepoPath $script:E2ERoot -Service $service + Update-ServicePom -SdkRepoPath $script:E2ERoot -Service $service -Module $module + Update-CiYml -SdkRepoPath $script:E2ERoot -Service $service -Module $module -GroupId $groupId + + # Root pom updated + $rootPom = Get-Content (Join-Path $script:E2ERoot "pom.xml") -Raw + $rootPom | Should -Match "sdk/compute" + + # Service pom created + $svcPom = Get-Content (Join-Path $script:E2ERoot "sdk" "compute" "pom.xml") -Raw + $svcPom | Should -Match "azure-resourcemanager-compute" + + # ci.yml created with artifact + $ciFile = Join-Path $script:E2ERoot "sdk" "compute" "ci.yml" + Test-Path $ciFile | Should -Be $true + $ciContent = Get-Content $ciFile -Raw + $ciContent | Should -Match "azure-resourcemanager-compute" + $ciContent | Should -Match "com.azure.resourcemanager" + $ciContent | Should -Match "ServiceDirectory: compute" + } +} From e6738727eade00233c99411dbd8f7e1558286d15 Mon Sep 17 00:00:00 2001 From: Haoling Dong Date: Wed, 25 Mar 2026 13:34:56 +0800 Subject: [PATCH 4/5] resolve comments --- eng/scripts/Automation-Sdk-UpdateMetadata.ps1 | 2 +- eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 index f99ff6fe2842..111fa4a6b3eb 100644 --- a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 +++ b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 @@ -76,7 +76,7 @@ try { Update-CiYml -SdkRepoPath $SdkRepoPath -Service $service -Module $module -GroupId $groupId } else { - LogInfo "[CI][Skip] No pom.xml found at package path, skipping ci.yml update" + LogError "No pom.xml found at '$packagePomPath'. Cannot determine groupId for ci.yml update." } LogInfo "" diff --git a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 index 87a516e425ee..a6bac4523bb0 100644 --- a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 +++ b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 @@ -235,10 +235,10 @@ Describe "Get-ServiceAndModuleFromPath" { $result.Module | Should -Be "azure-resourcemanager-network" } - It "Should extract service and module from forward-slash path" { + It "Should extract service and module from forward-slash path with different repo name" { $result = Get-ServiceAndModuleFromPath ` - -PackagePath "C:/repos/azure-sdk-for-java/sdk/storage/azure-storage-blob" ` - -SdkRepoPath "C:/repos/azure-sdk-for-java" + -PackagePath "C:/repos/my-java-sdk/sdk/storage/azure-storage-blob" ` + -SdkRepoPath "C:/repos/my-java-sdk" $result.Service | Should -Be "storage" $result.Module | Should -Be "azure-storage-blob" From 6d0ee3cde58927ed725a0e5ee299060b2d38085f Mon Sep 17 00:00:00 2001 From: Haoling Dong Date: Fri, 27 Mar 2026 13:21:59 +0800 Subject: [PATCH 5/5] Fail metadata update for unsupported SDK types Add groupId validation to reject unsupported SDK types (e.g., com.azure.spring). Only com.azure and com.azure.resourcemanager are supported. Uses LogError with the package path for clear diagnostics before exiting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/scripts/Automation-Sdk-UpdateMetadata.ps1 | 7 ++++ .../Automation-Sdk-UpdateMetadata.tests.ps1 | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 index 111fa4a6b3eb..2cc88bce84d0 100644 --- a/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 +++ b/eng/scripts/Automation-Sdk-UpdateMetadata.ps1 @@ -73,6 +73,13 @@ try { if (Test-Path $packagePomPath) { $groupId = Get-GroupIdFromPom -PomPath $packagePomPath LogInfo " GroupId: $groupId" + + $supportedGroupIds = @("com.azure", "com.azure.resourcemanager") + if ($groupId -notin $supportedGroupIds) { + LogError "Unsupported SDK type with groupId '$groupId' at '$PackagePath'. This script only supports: $($supportedGroupIds -join ', ')." + exit 1 + } + Update-CiYml -SdkRepoPath $SdkRepoPath -Service $service -Module $module -GroupId $groupId } else { diff --git a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 index a6bac4523bb0..5c9ea8ef1665 100644 --- a/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 +++ b/eng/scripts/tests/Automation-Sdk-UpdateMetadata.tests.ps1 @@ -1019,3 +1019,43 @@ Describe "End-to-End: New Service with CI update" { $ciContent | Should -Match "ServiceDirectory: compute" } } + +# ============================================================================ +# End-to-end: Unsupported SDK type should fail the run +# ============================================================================ +Describe "End-to-End: Unsupported SDK Type" { + BeforeEach { + $script:E2ERoot = Join-Path $script:TestRoot "e2e-unsupported-$(New-Guid)" + New-Item -ItemType Directory -Path $script:E2ERoot -Force | Out-Null + + Set-Content -Path (Join-Path $script:E2ERoot "pom.xml") -Value $script:SampleRootPomXml + $pkgDir = Join-Path $script:E2ERoot "sdk" "spring" "spring-cloud-azure-core" + New-Item -ItemType Directory -Path $pkgDir -Force | Out-Null + + $springPom = @" + + + 4.0.0 + com.azure.spring + spring-cloud-azure-core + 4.0.0 + +"@ + Set-Content -Path (Join-Path $pkgDir "pom.xml") -Value $springPom + } + + AfterEach { + if (Test-Path $script:E2ERoot) { + Remove-Item -Path $script:E2ERoot -Recurse -Force + } + } + + It "Should fail for unsupported groupId (e.g., com.azure.spring)" { + $scriptPath = Join-Path $PSScriptRoot ".." "Automation-Sdk-UpdateMetadata.ps1" + $pkgPath = Join-Path $script:E2ERoot "sdk" "spring" "spring-cloud-azure-core" + + $output = pwsh -NoProfile -File $scriptPath -PackagePath $pkgPath -SdkRepoPath $script:E2ERoot 2>&1 + $LASTEXITCODE | Should -Be 1 + $output | Out-String | Should -Match "Unsupported SDK type" + } +}