Skip to content

Commit ad69784

Browse files
committed
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-net into nirovins/switch_branches
2 parents d353931 + 8899058 commit ad69784

File tree

45 files changed

+1052
-20
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1052
-20
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,3 +1223,6 @@
12231223

12241224
#PRLabel: %CodeGen %Client
12251225
/eng/scripts/typespec/ @JoshLove-msft @m-nash @jorgerangel-msft @jsquire @live1206 @ArcturusZhang @ArthurMa1978
1226+
1227+
# Add owners for CI pipeline configuration changes
1228+
/sdk/**/ci*.yml @Azure/azure-sdk-write-net-core

eng/pipelines/templates/jobs/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
- "!SessionRecords"
6464
EnablePRGeneration: true
6565
PRMatrixSetting: ProjectNames
66-
PRJobBatchSize: 10
66+
PRJobBatchSize: 5
6767
AdditionalParameters:
6868
Artifacts: ${{ parameters.Artifacts }}
6969
TestPipeline: ${{ parameters.TestPipeline }}
@@ -190,7 +190,7 @@ jobs:
190190
- "!SessionRecords"
191191
EnablePRGeneration: true
192192
PRMatrixSetting: ProjectNames
193-
PRJobBatchSize: 20
193+
PRJobBatchSize: 10
194194
PRMatrixIndirectFilters:
195195
- 'AdditionalTestArguments=.*true'
196196
- 'Pool=.*LinuxPool$'

eng/scripts/CodeChecks.Helpers.ps1

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,58 @@
11
# Helper functions for CodeChecks.ps1
22
# Separated so they can be unit-tested without executing the main script.
33

4+
# Checks whether only CI pipeline config files (ci*.yml) were changed in the given
5+
# service directory relative to the merge base. Returns $true if all changed files
6+
# in sdk/<ServiceDirectory>/ match ci*.yml, meaning codegen/snippets/API export can be skipped.
7+
function Test-OnlyCiConfigChanged {
8+
param(
9+
[string]$ServiceDirectory,
10+
[string]$RepoRoot
11+
)
12+
13+
try {
14+
# Determine the merge base to compare against
15+
$targetBranch = $env:SYSTEM_PULLREQUEST_TARGETBRANCH
16+
$targetBranchName = $null
17+
if ($targetBranch) {
18+
$targetBranchName = $targetBranch -replace '^refs/heads/', ''
19+
}
20+
21+
# Try to find the merge base, falling back through common branch names
22+
$mergeBase = $null
23+
$candidates = @()
24+
if ($targetBranchName) {
25+
$candidates += "origin/$targetBranchName"
26+
$candidates += $targetBranchName
27+
}
28+
$candidates += "origin/main", "main", "origin/master", "master"
29+
30+
foreach ($candidate in $candidates) {
31+
$mergeBase = git -C $RepoRoot merge-base HEAD $candidate 2>$null
32+
if ($mergeBase) { break }
33+
}
34+
if (-not $mergeBase) { return $false }
35+
36+
$svcPath = "sdk/$ServiceDirectory/"
37+
$changedFiles = git -C $RepoRoot diff --name-only $mergeBase -- $svcPath 2>$null
38+
if (-not $changedFiles) { return $false }
39+
40+
foreach ($file in $changedFiles) {
41+
$fileName = Split-Path -Leaf $file
42+
if ($fileName -notmatch '^ci.*\.yml$') {
43+
return $false
44+
}
45+
}
46+
47+
return $true
48+
}
49+
catch {
50+
# On any error, fall back to running codegen (safe default)
51+
Write-Host "Warning: Could not determine changed files for $ServiceDirectory — running full checks. Error: $_"
52+
return $false
53+
}
54+
}
55+
456
# Parses a single git status --porcelain line and returns the path(s) it contains.
557
# For renames ("old -> new"), returns both paths. Returns an empty array for blank/malformed lines.
658
function Get-PorcelainPaths([string]$line) {
@@ -44,6 +96,153 @@ function Get-CodeCheckSummary {
4496
}
4597
}
4698

99+
# Validates that packages in the given service directory which are depended on by
100+
# other services have TestDependsOnDependency set in their CI file.
101+
# Returns an array of objects describing any missing entries, each with:
102+
# Package - the package name that needs to be covered
103+
# CiFile - the CI file path where TestDependsOnDependency should be added
104+
# Dependents - list of service directories that depend on this package
105+
function Get-MissingTestDependsOnDependency {
106+
param(
107+
[string]$ServiceDirectory,
108+
[string]$RepoRoot
109+
)
110+
111+
$sdkDir = Join-Path $RepoRoot "sdk"
112+
$svcDir = Join-Path $sdkDir $ServiceDirectory
113+
114+
if (-not (Test-Path $svcDir)) { return @() }
115+
116+
$coreInfra = @(
117+
'Azure.Core', 'Azure.Core.TestFramework', 'Azure.Core.Experimental',
118+
'Azure.Identity', 'Azure.ResourceManager',
119+
'System.ClientModel', 'System.ClientModel.SourceGeneration',
120+
'Microsoft.ClientModel.TestFramework'
121+
)
122+
123+
# Find all packages in THIS service directory
124+
$localPackages = @{}
125+
Get-ChildItem $svcDir -Directory | ForEach-Object {
126+
$srcDir = Join-Path $_.FullName "src"
127+
if (Test-Path $srcDir) {
128+
Get-ChildItem $srcDir -Filter "*.csproj" -File | ForEach-Object {
129+
$localPackages[$_.BaseName] = $true
130+
}
131+
}
132+
}
133+
134+
# Find which local packages are depended on by OTHER services' test projects
135+
$dependedOn = @{} # packageName -> Set of dependent service dirs
136+
$refPattern = '<(?:Package|Project)Reference[^>]*Include\s*=\s*"([^"]+)"'
137+
138+
foreach ($svcItem in (Get-ChildItem $sdkDir -Directory)) {
139+
$otherSvc = $svcItem.Name
140+
if ($otherSvc -eq $ServiceDirectory) { continue }
141+
142+
Get-ChildItem $svcItem.FullName -Recurse -Filter "*.csproj" | Where-Object {
143+
$_.FullName -match '[/\\]tests[/\\]'
144+
} | ForEach-Object {
145+
$content = Get-Content $_.FullName -Raw
146+
$matches2 = [regex]::Matches($content, $refPattern)
147+
foreach ($match in $matches2) {
148+
$refName = $match.Groups[1].Value
149+
if ($refName -match '[/\\]') {
150+
$refName = [System.IO.Path]::GetFileNameWithoutExtension(($refName -replace '\\', '/'))
151+
}
152+
if ($localPackages.ContainsKey($refName) -and $refName -notin $coreInfra) {
153+
if (-not $dependedOn.ContainsKey($refName)) {
154+
$dependedOn[$refName] = [System.Collections.Generic.HashSet[string]]::new()
155+
}
156+
[void]$dependedOn[$refName].Add($otherSvc)
157+
}
158+
}
159+
}
160+
}
161+
162+
if ($dependedOn.Count -eq 0) { return @() }
163+
164+
# Find which CI file each depended-on package is an artifact in
165+
$ciFiles = Get-ChildItem $svcDir -Filter "ci*.yml" -File
166+
$pkgToCiFile = @{}
167+
foreach ($ciFile in $ciFiles) {
168+
$content = Get-Content $ciFile.FullName -Raw
169+
foreach ($pkg in $dependedOn.Keys) {
170+
$escaped = [regex]::Escape($pkg)
171+
if ($content -match "(?m)^\s*-\s*name:\s*$escaped\s*$") {
172+
$pkgToCiFile[$pkg] = $ciFile.FullName
173+
}
174+
}
175+
}
176+
177+
# Check which are already covered by TestDependsOnDependency
178+
$missing = @()
179+
$ciFileCoverage = @{} # ciFilePath -> Set of covered package names
180+
foreach ($ciFile in $ciFiles) {
181+
$content = Get-Content $ciFile.FullName -Raw
182+
if ($content -match 'TestDependsOnDependency:\s*(.+)') {
183+
$coveredPkgs = $Matches[1].Trim() -split '\s+'
184+
$ciFileCoverage[$ciFile.FullName] = $coveredPkgs
185+
} else {
186+
$ciFileCoverage[$ciFile.FullName] = @()
187+
}
188+
}
189+
190+
foreach ($pkg in $dependedOn.Keys | Sort-Object) {
191+
$ciFile = $pkgToCiFile[$pkg]
192+
if (-not $ciFile) { continue }
193+
194+
$coveredInFile = $ciFileCoverage[$ciFile]
195+
if ($pkg -notin $coveredInFile) {
196+
$missing += [PSCustomObject]@{
197+
Package = $pkg
198+
CiFile = $ciFile
199+
Dependents = @($dependedOn[$pkg] | Sort-Object)
200+
}
201+
}
202+
}
203+
204+
return $missing
205+
}
206+
207+
# Adds missing TestDependsOnDependency entries to a CI YAML file.
208+
# If the file already has a TestDependsOnDependency line, merges the new packages.
209+
# If not, appends it at the end of the extends.parameters block.
210+
function Add-TestDependsOnDependency {
211+
param(
212+
[string]$CiFilePath,
213+
[string[]]$PackageNames
214+
)
215+
216+
$content = Get-Content $CiFilePath -Raw
217+
$lines = $content -split "`n"
218+
219+
if ($content -match 'TestDependsOnDependency:\s*(.+)') {
220+
$existing = $Matches[1].Trim() -split '\s+'
221+
$all = @($existing + $PackageNames) | Sort-Object -Unique
222+
$newValue = "TestDependsOnDependency: $($all -join ' ')"
223+
$content = $content -replace 'TestDependsOnDependency:\s*.+', $newValue
224+
} else {
225+
# Find indentation from extends.parameters block
226+
$indent = " "
227+
for ($i = 0; $i -lt $lines.Length; $i++) {
228+
if ($lines[$i].Trim() -eq 'parameters:' -and $i -gt 0 -and $lines[$i - 1].Trim() -match 'template:') {
229+
$paramIndent = $lines[$i].Length - $lines[$i].TrimStart().Length
230+
$indent = ' ' * ($paramIndent + 2)
231+
break
232+
}
233+
}
234+
235+
$newLine = "${indent}TestDependsOnDependency: $(($PackageNames | Sort-Object) -join ' ')"
236+
237+
# Append before the last line of the file (or at end)
238+
$trimmed = $content.TrimEnd()
239+
$content = "$trimmed`n$newLine`n"
240+
}
241+
242+
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
243+
[System.IO.File]::WriteAllText($CiFilePath, $content, $utf8NoBom)
244+
}
245+
47246
# Determines the action to take when a git diff is detected after code checks.
48247
# Returns an object with:
49248
# Action - "none" (no diffs), "error" (fail with message), or "report" (informational summary)

eng/scripts/CodeChecks.ps1

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ try {
8484
}
8585
if (-not $ProjectDirectory)
8686
{
87+
# In PR builds, check if only CI config files changed for this service directory.
88+
# If so, skip expensive codegen/snippet/API operations since ci*.yml changes
89+
# don't affect generated code. Only apply this optimization in PR builds —
90+
# release and CI push pipelines should always run the full checks.
91+
$onlyCiConfigChanged = $false
92+
if ($env:BUILD_REASON -eq "PullRequest") {
93+
$onlyCiConfigChanged = Test-OnlyCiConfigChanged -ServiceDirectory $ServiceDirectory -RepoRoot $RepoRoot
94+
if ($onlyCiConfigChanged) {
95+
Write-Host "`nOnly CI config files (ci*.yml) changed in sdk/$ServiceDirectory — skipping codegen, snippets, and API export."
96+
}
97+
}
98+
8799
Write-Host "Force .NET Welcome experience"
88100
Invoke-Block {
89101
& dotnet msbuild -version
@@ -110,7 +122,7 @@ try {
110122
}
111123
}
112124

113-
if ($SkipDiffValidation) {
125+
if ($SkipDiffValidation -and -not $onlyCiConfigChanged) {
114126
Write-Host "`nRunning dotnet format"
115127
Join-Path "$PSScriptRoot/../../sdk" $ServiceDirectory `
116128
| Resolve-Path `
@@ -123,17 +135,19 @@ try {
123135
}
124136
}
125137

126-
$debugLogging = $env:SYSTEM_DEBUG -eq "true"
127-
$logsFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY
128-
$diagnosticArguments = ($debugLogging -and $logsFolder) ? "/binarylogger:$logsFolder/generatecode.binlog" : ""
138+
if (-not $onlyCiConfigChanged) {
139+
$debugLogging = $env:SYSTEM_DEBUG -eq "true"
140+
$logsFolder = $env:BUILD_ARTIFACTSTAGINGDIRECTORY
141+
$diagnosticArguments = ($debugLogging -and $logsFolder) ? "/binarylogger:$logsFolder/generatecode.binlog" : ""
129142

130-
Write-Host "Re-generating clients"
131-
Invoke-Block {
132-
& dotnet msbuild $PSScriptRoot\..\service.proj /restore /t:GenerateCode /p:SDKType=$SDKType /p:ServiceDirectory=$ServiceDirectory $diagnosticArguments /p:ProjectListOverrideFile=""
143+
Write-Host "Re-generating clients"
144+
Invoke-Block {
145+
& dotnet msbuild $PSScriptRoot\..\service.proj /restore /t:GenerateCode /p:SDKType=$SDKType /p:ServiceDirectory=$ServiceDirectory $diagnosticArguments /p:ProjectListOverrideFile=""
146+
}
133147
}
134148
}
135149

136-
if ($ServiceDirectory -ne "tools") {
150+
if ($ServiceDirectory -ne "tools" -and -not $onlyCiConfigChanged) {
137151
Write-Host "Re-generating snippets"
138152
Invoke-Block {
139153
& $PSScriptRoot\Update-Snippets.ps1 -ServiceDirectory $ServiceDirectory
@@ -144,7 +158,7 @@ try {
144158
& $PSScriptRoot\Export-API.ps1 -ServiceDirectory $ServiceDirectory -SDKType $SDKType -SpellCheckPublicApiSurface:$SpellCheckPublicApiSurface
145159
}
146160
}
147-
else {
161+
elseif ($ServiceDirectory -eq "tools") {
148162
Write-Host "Skipping snippet and API listing generation for tools directory"
149163
}
150164

@@ -216,6 +230,34 @@ try {
216230
}
217231
}
218232

233+
Write-Host "`nValidating TestDependsOnDependency coverage"
234+
$missingDeps = Get-MissingTestDependsOnDependency -ServiceDirectory $ServiceDirectory -RepoRoot $RepoRoot
235+
if ($missingDeps) {
236+
if ($SkipDiffValidation) {
237+
# Auto-fix: group by CI file and add the missing entries
238+
$grouped = $missingDeps | Group-Object -Property CiFile
239+
foreach ($group in $grouped) {
240+
$ciFile = $group.Name
241+
$pkgs = @($group.Group | ForEach-Object { $_.Package })
242+
Write-Host " Adding TestDependsOnDependency to $(Split-Path -Leaf $ciFile): $($pkgs -join ', ')"
243+
Add-TestDependsOnDependency -CiFilePath $ciFile -PackageNames $pkgs
244+
}
245+
} else {
246+
foreach ($dep in $missingDeps) {
247+
$relPath = [System.IO.Path]::GetRelativePath($RepoRoot, $dep.CiFile) -replace '\\', '/'
248+
$dependentList = $dep.Dependents -join ', '
249+
LogError `
250+
"Package '$($dep.Package)' is depended on by tests in other services ($dependentList) `
251+
but is not listed in TestDependsOnDependency in '$relPath'.`
252+
Add '$($dep.Package)' to the TestDependsOnDependency parameter in '$relPath'.`
253+
Example: TestDependsOnDependency: $($dep.Package)`
254+
This ensures that when '$($dep.Package)' changes, downstream dependent tests are run.`
255+
`
256+
To fix this locally, run 'eng\scripts\CodeChecks.ps1 -ServiceDirectory $ServiceDirectory -SkipDiffValidation' and commit the resulting changes."
257+
}
258+
}
259+
}
260+
219261
if (-not $ProjectDirectory)
220262
{
221263
Write-Host "git diff"

0 commit comments

Comments
 (0)