From b2f5c277aad5edf5e9fdcd403559e9b380e7cae5 Mon Sep 17 00:00:00 2001 From: Aditya Pujara <59631311+a0x1ab@users.noreply.github.com> Date: Sun, 15 Mar 2026 11:36:30 +1030 Subject: [PATCH 1/7] added changes for squad mapping --- .azure-pipelines/sync-squad-mapping.yml | 94 +++++++++++++ tools/Github/ParseSquadMappingList.ps1 | 177 ++++++++++++++++++++++++ 2 files changed, 271 insertions(+) create mode 100644 .azure-pipelines/sync-squad-mapping.yml create mode 100644 tools/Github/ParseSquadMappingList.ps1 diff --git a/.azure-pipelines/sync-squad-mapping.yml b/.azure-pipelines/sync-squad-mapping.yml new file mode 100644 index 00000000000..8ce52254979 --- /dev/null +++ b/.azure-pipelines/sync-squad-mapping.yml @@ -0,0 +1,94 @@ +name: Azure CLI Sync Squad Mapping + +schedules: +- cron: "20 16 * * 0" + displayName: 12:20 AM (UTC + 8:00) China Weekly Run + branches: + include: + - dev + +resources: + repositories: + - repository: SquadMappingWiki + type: git + name: internal.wiki + +variables: +- template: ${{ variables.Pipeline.Workspace }}/.azure-pipelines/templates/variables.yml + +jobs: +- job: UpdateYaml + displayName: Update resourceManagement.yml with squad labels + pool: + name: ${{ variables.windows_pool }} + uses: + repositories: + - SquadMappingWiki + + steps: + - task: UseDotNet@2 + displayName: Install .NET 8 SDK + inputs: + packageType: sdk + version: 8.0.x + + - pwsh: | + dotnet --version + dotnet new tool-manifest --force + dotnet tool install powershell --version 7.4.* + displayName: Install PowerShell 7.4.x + + - pwsh: | + dotnet tool run pwsh -NoLogo -NoProfile -NonInteractive -File ./tools/Github/ParseSquadMappingList.ps1 -AccessToken $env:SYSTEM_ACCESSTOKEN + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + displayName: Update resourceManagement.yml file locally + + - pwsh: | + $hasChanges = git diff --name-only .github/policies + if ($null -eq $hasChanges) { + Write-Host "The wiki has no changes." + Write-Host "##vso[task.setvariable variable=ChangesDetected]false" + } else { + Write-Host "There are changes in the wiki." + Write-Host "##vso[task.setvariable variable=ChangesDetected]true" + } + displayName: Check if Wiki table has any changes + + - task: AzurePowerShell@5 + inputs: + pwsh: true + azureSubscription: '$(AZURE_SDK_INFRA_SUB_CONNECTED_SERVICE)' + ScriptType: 'InlineScript' + Inline: | + $GithubToken = Get-AzKeyVaultSecret -VaultName $(GithubPATKeyVaultName) -Name $(GithubPATKeyVaultAccount) -AsPlainText + Write-Host "##vso[task.setvariable variable=GithubToken;issecret=true]$GithubToken" + azurePowerShellVersion: 'LatestVersion' + displayName: Get Github PAT from Key Vault + + - pwsh: | + git config --global user.email "AzPyCLI@microsoft.com" + git config --global user.name "Azure CLI Team" + git checkout -b "sync_squad_mapping_$env:Build_BuildId" + + git add .github/policies + git commit -m "Sync resourceManagement.yml for squad mapping" + + git remote add azclibot https://azclibot:$(GithubToken)@github.com/azclibot/azure-cli.git + git push azclibot "sync_squad_mapping_$env:Build_BuildId" --force + displayName: Git commit and push + condition: and(succeeded(), eq(variables['ChangesDetected'], 'true')) + + - pwsh: | + $Title = "{CI} Sync squad mapping labels from ADO Wiki to resourceManagement.yml" + $HeadBranch = "azclibot:sync_squad_mapping_$env:Build_BuildId" + $BaseBranch = "dev" + $Description = "This PR synchronizes squad labels in resourceManagement.yml based on the Squad Mapping ADO wiki page." + + $Headers = @{"Accept" = "application/vnd.github+json"; "Authorization" = "Bearer $(GithubToken)" } + $RequestBody = @{"title" = $Title; "body" = $Description; "head" = $HeadBranch; "base" = $BaseBranch;} + $Uri = "https://api.github.com/repos/Azure/azure-cli/pulls" + + Invoke-WebRequest -Uri $Uri -Method POST -Headers $Headers -Body ($RequestBody | ConvertTo-Json) + displayName: Create PR to azure/azure-cli dev branch + condition: and(succeeded(), eq(variables['ChangesDetected'], 'true')) diff --git a/tools/Github/ParseSquadMappingList.ps1 b/tools/Github/ParseSquadMappingList.ps1 new file mode 100644 index 00000000000..db060ed959c --- /dev/null +++ b/tools/Github/ParseSquadMappingList.ps1 @@ -0,0 +1,177 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- + +<#[ +.SYNOPSIS + Sync ADO Wiki Squad Mapping to resourceManagement.yml by adding squad labels + wherever a mapped label is added. +#> +param( + [Parameter(Mandatory = $true)] + [string] $AccessToken +) + +function InitializeRequiredPackages { + [CmdletBinding()] + param () + + $packagesDirectoryName = "JsonYamlPackages" + $packagesDirectory = Join-Path -Path . -ChildPath $packagesDirectoryName + if (Test-Path -LiteralPath $packagesDirectory) { + Remove-Item -LiteralPath $packagesDirectory -Recurse -Force + } + + New-Item -Path . -Name $packagesDirectoryName -ItemType Directory -Force + + $requiredPackages = @( + @{ PackageName = "Newtonsoft.Json"; PackageVersion = "13.0.2"; DllName = "Newtonsoft.Json.dll" }, + @{ PackageName = "YamlDotNet"; PackageVersion = "13.2.0"; DllName = "YamlDotNet.dll" } + ) + + $requiredPackages | ForEach-Object { + $packageName = $_["PackageName"] + $packageVersion = $_["PackageVersion"] + $packageDll = $_["DllName"] + Install-Package -Name $packageName -RequiredVersion $packageVersion -Source "https://www.nuget.org/api/v2" -Destination $packagesDirectory -SkipDependencies -ExcludeVersion -Force + Add-Type -LiteralPath (Join-Path -Path $packagesDirectory -ChildPath $packageName | Join-Path -ChildPath "lib" | Join-Path -ChildPath "net6.0" | Join-Path -ChildPath $packageDll) -ErrorAction SilentlyContinue + } +} + +function GetSquadMappingFromWiki { + [CmdletBinding()] + param( + [string] $AccessToken + ) + + $username = "" + $password = $AccessToken + $pair = "{0}:{1}" -f ($username, $password) + $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) + $token = [System.Convert]::ToBase64String($bytes) + $headers = @{ Authorization = "Basic {0}" -f ($token) } + + $response = Invoke-RestMethod 'https://dev.azure.com/azclitools/internal/_apis/wiki/wikis/internal.wiki/pages?path=/Squad%20Mapping&includeContent=true' -Headers $headers -ErrorAction Stop + $rows = ($response.content -split "\n") | Where-Object { $_ -like '|*' } | Select-Object -Skip 2 + + $mapping = @{} + foreach ($row in $rows) { + $columns = $row -split "\|" + if ($columns.Count -lt 3) { + continue + } + + $label = $columns[1].Trim() + $squad = $columns[2].Trim() + if (![string]::IsNullOrWhiteSpace($label) -and ![string]::IsNullOrWhiteSpace($squad)) { + $mapping[$label] = $squad + } + } + + return $mapping +} + +function EnsureSquadLabelsInActions { + [CmdletBinding()] + param( + [object] $ActionList, + [hashtable] $LabelToSquad + ) + + if ($null -eq $ActionList) { + return $ActionList + } + + $list = [System.Collections.Generic.List[object]]::new() + foreach ($action in $ActionList) { + $list.Add($action) + } + + $labelsPresent = @{} + foreach ($action in $list) { + if ($null -ne $action -and $action.PSObject.Properties.Name -contains "addLabel") { + $label = $action.addLabel.label + if (![string]::IsNullOrWhiteSpace($label)) { + $labelsPresent[$label] = $true + } + } + } + + foreach ($label in $labelsPresent.Keys) { + if ($LabelToSquad.ContainsKey($label)) { + $squadLabel = $LabelToSquad[$label] + if (-not $labelsPresent.ContainsKey($squadLabel)) { + $list.Add([PSCustomObject]@{ addLabel = [PSCustomObject]@{ label = $squadLabel } }) + $labelsPresent[$squadLabel] = $true + } + } + } + + return $list.ToArray() +} + +function UpdateNode { + [CmdletBinding()] + param( + [object] $Node, + [hashtable] $LabelToSquad + ) + + if ($null -eq $Node) { + return + } + + if ($Node -is [System.Collections.IDictionary] -or $Node -is [PSCustomObject]) { + foreach ($property in $Node.PSObject.Properties) { + $name = $property.Name + $value = $property.Value + + if ($name -in @('then', 'actions')) { + $Node.$name = EnsureSquadLabelsInActions -ActionList $value -LabelToSquad $LabelToSquad + } + + UpdateNode -Node $value -LabelToSquad $LabelToSquad + } + return + } + + if ($Node -is [System.Collections.IEnumerable] -and -not ($Node -is [string])) { + foreach ($item in $Node) { + UpdateNode -Node $item -LabelToSquad $LabelToSquad + } + } +} + +$labelToSquad = GetSquadMappingFromWiki -AccessToken $AccessToken +if ($labelToSquad.Count -eq 0) { + throw "No squad mappings found in the wiki." +} + +InitializeRequiredPackages + +$yamlConfigPath = $PSScriptRoot | Split-Path | Split-Path | Join-Path -ChildPath ".github" | Join-Path -ChildPath "policies" | Join-Path -ChildPath "resourceManagement.yml" +$yamlContent = Get-Content -Path $yamlConfigPath -Raw +$yamlDeserializer = [YamlDotNet.Serialization.DeserializerBuilder]::new().Build() +$yamlObjectGraph = $yamlDeserializer.Deserialize($yamlContent) +$jsonSerializer = [YamlDotNet.Serialization.SerializerBuilder]::new().JsonCompatible().Build() +$jsonObjectGraph = $jsonSerializer.Serialize($yamlObjectGraph) | ConvertFrom-Json + +UpdateNode -Node $jsonObjectGraph -LabelToSquad $labelToSquad + +$updatedJsonContent = $jsonObjectGraph | ConvertTo-Json -Depth 64 +$updatedJsonObjectGraph = [Newtonsoft.Json.JsonConvert]::DeserializeObject[System.Dynamic.ExpandoObject]($updatedJsonContent) +$yamlSerializer = [YamlDotNet.Serialization.SerializerBuilder]::new().Build() +$updatedYamlContent = $yamlSerializer.Serialize($updatedJsonObjectGraph) +$updatedYamlContent | Out-File -FilePath $yamlConfigPath -NoNewline -Force + +(Get-Content -Path $yamlConfigPath) | ForEach-Object { $_.TrimEnd() } | Set-Content -Path $yamlConfigPath From dc3413e8fac519ad4c0d12f9401f57777e804f57 Mon Sep 17 00:00:00 2001 From: Aditya Pujara <59631311+a0x1ab@users.noreply.github.com> Date: Mon, 16 Mar 2026 16:49:18 +1030 Subject: [PATCH 2/7] added changes per suggestion --- .azure-pipelines/sync-squad-mapping.yml | 1 + tools/Github/ParseSquadMappingList.ps1 | 24 +++++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.azure-pipelines/sync-squad-mapping.yml b/.azure-pipelines/sync-squad-mapping.yml index 8ce52254979..c810452fd1f 100644 --- a/.azure-pipelines/sync-squad-mapping.yml +++ b/.azure-pipelines/sync-squad-mapping.yml @@ -65,6 +65,7 @@ jobs: Write-Host "##vso[task.setvariable variable=GithubToken;issecret=true]$GithubToken" azurePowerShellVersion: 'LatestVersion' displayName: Get Github PAT from Key Vault + condition: and(succeeded(), eq(variables['ChangesDetected'], 'true')) - pwsh: | git config --global user.email "AzPyCLI@microsoft.com" diff --git a/tools/Github/ParseSquadMappingList.ps1 b/tools/Github/ParseSquadMappingList.ps1 index db060ed959c..3052511f742 100644 --- a/tools/Github/ParseSquadMappingList.ps1 +++ b/tools/Github/ParseSquadMappingList.ps1 @@ -12,7 +12,7 @@ # limitations under the License. # ---------------------------------------------------------------------------------- -<#[ +<# .SYNOPSIS Sync ADO Wiki Squad Mapping to resourceManagement.yml by adding squad labels wherever a mapped label is added. @@ -44,7 +44,11 @@ function InitializeRequiredPackages { $packageVersion = $_["PackageVersion"] $packageDll = $_["DllName"] Install-Package -Name $packageName -RequiredVersion $packageVersion -Source "https://www.nuget.org/api/v2" -Destination $packagesDirectory -SkipDependencies -ExcludeVersion -Force - Add-Type -LiteralPath (Join-Path -Path $packagesDirectory -ChildPath $packageName | Join-Path -ChildPath "lib" | Join-Path -ChildPath "net6.0" | Join-Path -ChildPath $packageDll) -ErrorAction SilentlyContinue + $packageDllPath = Join-Path -Path $packagesDirectory -ChildPath $packageName | Join-Path -ChildPath "lib" | Join-Path -ChildPath "net6.0" | Join-Path -ChildPath $packageDll + if (-not (Test-Path -LiteralPath $packageDllPath)) { + throw "Package DLL not found: $packageDllPath" + } + Add-Type -LiteralPath $packageDllPath -ErrorAction Stop } } @@ -131,7 +135,21 @@ function UpdateNode { return } - if ($Node -is [System.Collections.IDictionary] -or $Node -is [PSCustomObject]) { + if ($Node -is [System.Collections.IDictionary]) { + foreach ($entry in $Node.GetEnumerator()) { + $name = $entry.Key + $value = $entry.Value + + if ($name -in @('then', 'actions')) { + $Node[$name] = EnsureSquadLabelsInActions -ActionList $value -LabelToSquad $LabelToSquad + } + + UpdateNode -Node $value -LabelToSquad $LabelToSquad + } + return + } + + if ($Node -is [PSCustomObject]) { foreach ($property in $Node.PSObject.Properties) { $name = $property.Name $value = $property.Value From c328cdd9c637d7ed888ad6d0154412b433e87b82 Mon Sep 17 00:00:00 2001 From: Aditya Pujara <59631311+a0x1ab@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:30:32 +1030 Subject: [PATCH 3/7] updated parsing file --- tools/Github/ParseSquadMappingList.ps1 | 212 +++++++++++++++++++------ 1 file changed, 166 insertions(+), 46 deletions(-) diff --git a/tools/Github/ParseSquadMappingList.ps1 b/tools/Github/ParseSquadMappingList.ps1 index 3052511f742..741e16a2d4f 100644 --- a/tools/Github/ParseSquadMappingList.ps1 +++ b/tools/Github/ParseSquadMappingList.ps1 @@ -22,34 +22,30 @@ param( [string] $AccessToken ) -function InitializeRequiredPackages { +function GetIndentLength { [CmdletBinding()] - param () - - $packagesDirectoryName = "JsonYamlPackages" - $packagesDirectory = Join-Path -Path . -ChildPath $packagesDirectoryName - if (Test-Path -LiteralPath $packagesDirectory) { - Remove-Item -LiteralPath $packagesDirectory -Recurse -Force - } + param( + [string] $Line + ) - New-Item -Path . -Name $packagesDirectoryName -ItemType Directory -Force + return ([regex]::Match($Line, '^\s*').Value).Length +} - $requiredPackages = @( - @{ PackageName = "Newtonsoft.Json"; PackageVersion = "13.0.2"; DllName = "Newtonsoft.Json.dll" }, - @{ PackageName = "YamlDotNet"; PackageVersion = "13.2.0"; DllName = "YamlDotNet.dll" } +function TryParseLabelValue { + [CmdletBinding()] + param( + [string] $Line ) - $requiredPackages | ForEach-Object { - $packageName = $_["PackageName"] - $packageVersion = $_["PackageVersion"] - $packageDll = $_["DllName"] - Install-Package -Name $packageName -RequiredVersion $packageVersion -Source "https://www.nuget.org/api/v2" -Destination $packagesDirectory -SkipDependencies -ExcludeVersion -Force - $packageDllPath = Join-Path -Path $packagesDirectory -ChildPath $packageName | Join-Path -ChildPath "lib" | Join-Path -ChildPath "net6.0" | Join-Path -ChildPath $packageDll - if (-not (Test-Path -LiteralPath $packageDllPath)) { - throw "Package DLL not found: $packageDllPath" + if ($Line -match '^\s*label:\s*(.+)\s*$') { + $value = $Matches[1].Trim() + if (($value.StartsWith('"') -and $value.EndsWith('"')) -or ($value.StartsWith("'") -and $value.EndsWith("'"))) { + $value = $value.Substring(1, $value.Length - 2) } - Add-Type -LiteralPath $packageDllPath -ErrorAction Stop + return $value } + + return $null } function GetSquadMappingFromWiki { @@ -92,35 +88,62 @@ function EnsureSquadLabelsInActions { [hashtable] $LabelToSquad ) - if ($null -eq $ActionList) { + if ($null -eq $ActionList -or $ActionList -is [string]) { return $ActionList } + $isSingleAction = $ActionList -is [System.Collections.IDictionary] -or $ActionList -is [PSCustomObject] $list = [System.Collections.Generic.List[object]]::new() - foreach ($action in $ActionList) { - $list.Add($action) + if ($isSingleAction) { + $list.Add($ActionList) + } else { + foreach ($action in $ActionList) { + $list.Add($action) + } } $labelsPresent = @{} foreach ($action in $list) { - if ($null -ne $action -and $action.PSObject.Properties.Name -contains "addLabel") { - $label = $action.addLabel.label - if (![string]::IsNullOrWhiteSpace($label)) { - $labelsPresent[$label] = $true + if ($null -eq $action) { + continue + } + + $label = $null + if ($action -is [System.Collections.IDictionary]) { + if ($action.Contains("addLabel")) { + $labelNode = $action["addLabel"] + if ($labelNode -is [System.Collections.IDictionary]) { + $label = $labelNode["label"] + } else { + $label = $labelNode.label + } } + } elseif ($action.PSObject.Properties.Name -contains "addLabel") { + $label = $action.addLabel.label + } + + if (![string]::IsNullOrWhiteSpace($label)) { + $labelsPresent[$label] = $true } } - foreach ($label in $labelsPresent.Keys) { + $labelsToCheck = @($labelsPresent.Keys) + $didAdd = $false + foreach ($label in $labelsToCheck) { if ($LabelToSquad.ContainsKey($label)) { $squadLabel = $LabelToSquad[$label] if (-not $labelsPresent.ContainsKey($squadLabel)) { $list.Add([PSCustomObject]@{ addLabel = [PSCustomObject]@{ label = $squadLabel } }) $labelsPresent[$squadLabel] = $true + $didAdd = $true } } } + if (-not $didAdd) { + return $ActionList + } + return $list.ToArray() } @@ -170,26 +193,123 @@ function UpdateNode { } } +function AddSquadLabelsToYaml { + [CmdletBinding()] + param( + [string[]] $Lines, + [hashtable] $LabelToSquad + ) + + $insertions = [System.Collections.Generic.List[object]]::new() + + for ($i = 0; $i -lt $Lines.Count; $i++) { + $line = $Lines[$i] + if ($line -match '^(\s*)(then|actions):\s*$') { + $baseIndentLength = $Matches[1].Length + + $j = $i + 1 + while ($j -lt $Lines.Count -and $Lines[$j].Trim().Length -eq 0) { + $j++ + } + if ($j -ge $Lines.Count) { + continue + } + + $listLine = $Lines[$j] + $listIndentLength = GetIndentLength -Line $listLine + if ($listIndentLength -lt $baseIndentLength -or -not ($listLine -match '^\s*-\s+')) { + continue + } + + $k = $j + while ($k -lt $Lines.Count) { + $lineAtK = $Lines[$k] + if ($lineAtK.Trim().Length -ne 0 -and (GetIndentLength -Line $lineAtK) -lt $baseIndentLength) { + break + } + # Also break if we hit a `description:` at the same level as `then:` + if ($lineAtK -match '^\s*description:' -and (GetIndentLength -Line $lineAtK) -eq $baseIndentLength) { + break + } + $k++ + } + + $labelsPresent = @{} + $lastAddLabelEnd = -1 + for ($b = $j; $b -lt $k; $b++) { + $lineAtB = $Lines[$b] + if ($lineAtB -match '^\s*-\s+addLabel:\s*$' -and (GetIndentLength -Line $lineAtB) -eq $listIndentLength) { + $labelValue = $null + for ($c = $b + 1; $c -lt $k; $c++) { + $lineAtC = $Lines[$c] + if ($lineAtC -match '^\s*-\s+' -and (GetIndentLength -Line $lineAtC) -eq $listIndentLength) { + break + } + $labelValue = TryParseLabelValue -Line $lineAtC + if ($null -ne $labelValue) { + $lastAddLabelEnd = $c + break + } + } + if (![string]::IsNullOrWhiteSpace($labelValue)) { + $labelsPresent[$labelValue] = $true + } + } + } + + if ($lastAddLabelEnd -ge 0) { + $labelsToAdd = [System.Collections.Generic.List[string]]::new() + foreach ($label in $labelsPresent.Keys) { + if ($LabelToSquad.ContainsKey($label)) { + $squadLabel = $LabelToSquad[$label] + if (-not $labelsPresent.ContainsKey($squadLabel) -and -not $labelsToAdd.Contains($squadLabel)) { + $labelsToAdd.Add($squadLabel) + } + } + } + + if ($labelsToAdd.Count -gt 0) { + $insertLines = [System.Collections.Generic.List[string]]::new() + foreach ($squadLabel in $labelsToAdd) { + $insertLines.Add((" " * $listIndentLength) + "- addLabel:") + $insertLines.Add((" " * $listIndentLength) + " label: $squadLabel") + } + $insertions.Add([PSCustomObject]@{ Index = $lastAddLabelEnd + 1; Lines = $insertLines }) + } + } + } + } + + if ($insertions.Count -eq 0) { + return $Lines + } + + $sortedInsertions = $insertions | Sort-Object Index -Descending + $lineList = [System.Collections.Generic.List[string]]::new() + $lineList.AddRange($Lines) + foreach ($insertion in $sortedInsertions) { + $lineList.InsertRange($insertion.Index, $insertion.Lines) + } + + return $lineList.ToArray() +} + $labelToSquad = GetSquadMappingFromWiki -AccessToken $AccessToken if ($labelToSquad.Count -eq 0) { throw "No squad mappings found in the wiki." } -InitializeRequiredPackages - $yamlConfigPath = $PSScriptRoot | Split-Path | Split-Path | Join-Path -ChildPath ".github" | Join-Path -ChildPath "policies" | Join-Path -ChildPath "resourceManagement.yml" -$yamlContent = Get-Content -Path $yamlConfigPath -Raw -$yamlDeserializer = [YamlDotNet.Serialization.DeserializerBuilder]::new().Build() -$yamlObjectGraph = $yamlDeserializer.Deserialize($yamlContent) -$jsonSerializer = [YamlDotNet.Serialization.SerializerBuilder]::new().JsonCompatible().Build() -$jsonObjectGraph = $jsonSerializer.Serialize($yamlObjectGraph) | ConvertFrom-Json - -UpdateNode -Node $jsonObjectGraph -LabelToSquad $labelToSquad - -$updatedJsonContent = $jsonObjectGraph | ConvertTo-Json -Depth 64 -$updatedJsonObjectGraph = [Newtonsoft.Json.JsonConvert]::DeserializeObject[System.Dynamic.ExpandoObject]($updatedJsonContent) -$yamlSerializer = [YamlDotNet.Serialization.SerializerBuilder]::new().Build() -$updatedYamlContent = $yamlSerializer.Serialize($updatedJsonObjectGraph) -$updatedYamlContent | Out-File -FilePath $yamlConfigPath -NoNewline -Force - -(Get-Content -Path $yamlConfigPath) | ForEach-Object { $_.TrimEnd() } | Set-Content -Path $yamlConfigPath +$yamlContent = [System.IO.File]::ReadAllText($yamlConfigPath) +$lineEnding = "`n" +if ($yamlContent.Contains("`r`n")) { + $lineEnding = "`r`n" +} +$endsWithNewline = $yamlContent.EndsWith("`n") +$yamlLines = [regex]::Split($yamlContent, "\r?\n", [System.Text.RegularExpressions.RegexOptions]::None) +$updatedLines = AddSquadLabelsToYaml -Lines $yamlLines -LabelToSquad $labelToSquad +$updatedContent = [string]::Join($lineEnding, $updatedLines) +if (-not $endsWithNewline -and $updatedContent.EndsWith($lineEnding)) { + $updatedContent = $updatedContent.Substring(0, $updatedContent.Length - $lineEnding.Length) +} +[System.IO.File]::WriteAllText($yamlConfigPath, $updatedContent) From 62d4592f78e75e22d6ad21e6b43f96ff2ebbf77c Mon Sep 17 00:00:00 2001 From: Aditya Pujara <59631311+a0x1ab@users.noreply.github.com> Date: Fri, 20 Mar 2026 14:04:12 +1030 Subject: [PATCH 4/7] updated parse squad mapping list script --- tools/Github/ParseSquadMappingList.ps1 | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tools/Github/ParseSquadMappingList.ps1 b/tools/Github/ParseSquadMappingList.ps1 index 741e16a2d4f..21db7432d74 100644 --- a/tools/Github/ParseSquadMappingList.ps1 +++ b/tools/Github/ParseSquadMappingList.ps1 @@ -275,6 +275,39 @@ function AddSquadLabelsToYaml { $insertLines.Add((" " * $listIndentLength) + " label: $squadLabel") } $insertions.Add([PSCustomObject]@{ Index = $lastAddLabelEnd + 1; Lines = $insertLines }) + + $isPR = $false + for ($p = $i - 1; $p -ge 0; $p--) { + if ($Lines[$p] -match '^\s*description:') { break } + if ($Lines[$p] -match 'payloadType:\s*Pull_Request') { $isPR = $true; break } + } + if ($isPR) { + $lastUserEnd = -1 + $usersIndent = $listIndentLength + 4 + $existingUsers = @{} + for ($b = $j; $b -lt $k; $b++) { + if ($Lines[$b] -match '^\s*-\s+assignTo:\s*$' -and (GetIndentLength -Line $Lines[$b]) -eq $listIndentLength) { + for ($c = $b + 1; $c -lt $k; $c++) { + if ($Lines[$c] -match '^\s*-\s+' -and (GetIndentLength -Line $Lines[$c]) -eq $listIndentLength) { break } + if ($Lines[$c] -match '^\s*-\s+(\S+)\s*$' -and (GetIndentLength -Line $Lines[$c]) -eq $usersIndent) { + $existingUsers[$Matches[1]] = $true + $lastUserEnd = $c + } + } + } + } + if ($lastUserEnd -ge 0) { + $userInsertLines = [System.Collections.Generic.List[string]]::new() + foreach ($squadLabel in $labelsToAdd) { + if (-not $existingUsers.ContainsKey($squadLabel)) { + $userInsertLines.Add((" " * $usersIndent) + "- $squadLabel") + } + } + if ($userInsertLines.Count -gt 0) { + $insertions.Add([PSCustomObject]@{ Index = $lastUserEnd + 1; Lines = $userInsertLines }) + } + } + } } } } From 40b45b0198bf544c8007f73818f00fea77c3aacb Mon Sep 17 00:00:00 2001 From: Aditya Pujara <59631311+a0x1ab@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:17:15 +1030 Subject: [PATCH 5/7] added changes to mention teams, change assignment to reviewer for squads --- tools/Github/ParseSquadMappingList.ps1 | 112 ++++++++++++++++++++----- 1 file changed, 89 insertions(+), 23 deletions(-) diff --git a/tools/Github/ParseSquadMappingList.ps1 b/tools/Github/ParseSquadMappingList.ps1 index 21db7432d74..780ca129549 100644 --- a/tools/Github/ParseSquadMappingList.ps1 +++ b/tools/Github/ParseSquadMappingList.ps1 @@ -193,6 +193,27 @@ function UpdateNode { } } +function GetLabelsFromConditions { + [CmdletBinding()] + param([string[]] $Lines, [int] $ThenLineIndex, [int] $BaseIndentLength) + $labels = [System.Collections.Generic.List[string]]::new() + $ifLineIndex = -1 + for ($p = $ThenLineIndex - 1; $p -ge 0; $p--) { + $lineAtP = $Lines[$p] + $indentAtP = GetIndentLength -Line $lineAtP + if ($lineAtP -match '^\s*-\s+if:\s*$' -and $indentAtP -le $BaseIndentLength) { $ifLineIndex = $p; break } + if ($indentAtP -lt $BaseIndentLength - 2) { break } + } + if ($ifLineIndex -lt 0) { return $labels } + for ($q = $ifLineIndex + 1; $q -lt $ThenLineIndex; $q++) { + $candidate = TryParseLabelValue -Line $Lines[$q] + if ($null -ne $candidate -and -not [string]::IsNullOrWhiteSpace($candidate)) { + if (-not $labels.Contains($candidate)) { $labels.Add($candidate) } + } + } + return $labels +} + function AddSquadLabelsToYaml { [CmdletBinding()] param( @@ -257,24 +278,32 @@ function AddSquadLabelsToYaml { } } - if ($lastAddLabelEnd -ge 0) { - $labelsToAdd = [System.Collections.Generic.List[string]]::new() - foreach ($label in $labelsPresent.Keys) { - if ($LabelToSquad.ContainsKey($label)) { - $squadLabel = $LabelToSquad[$label] - if (-not $labelsPresent.ContainsKey($squadLabel) -and -not $labelsToAdd.Contains($squadLabel)) { - $labelsToAdd.Add($squadLabel) - } + $labelsFromConditions = GetLabelsFromConditions -Lines $Lines -ThenLineIndex $i -BaseIndentLength $baseIndentLength + foreach ($condLabel in $labelsFromConditions) { + if (-not [string]::IsNullOrWhiteSpace($condLabel)) { + $labelsPresent[$condLabel] = $true + } + } + + $labelsToAdd = [System.Collections.Generic.List[string]]::new() + foreach ($label in $labelsPresent.Keys) { + if ($LabelToSquad.ContainsKey($label)) { + $squadLabel = $LabelToSquad[$label] + if (-not $labelsPresent.ContainsKey($squadLabel) -and -not $labelsToAdd.Contains($squadLabel)) { + $labelsToAdd.Add($squadLabel) } } + } - if ($labelsToAdd.Count -gt 0) { + if ($labelsToAdd.Count -gt 0) { + if ($lastAddLabelEnd -ge 0) { $insertLines = [System.Collections.Generic.List[string]]::new() foreach ($squadLabel in $labelsToAdd) { $insertLines.Add((" " * $listIndentLength) + "- addLabel:") $insertLines.Add((" " * $listIndentLength) + " label: $squadLabel") } $insertions.Add([PSCustomObject]@{ Index = $lastAddLabelEnd + 1; Lines = $insertLines }) + } $isPR = $false for ($p = $i - 1; $p -ge 0; $p--) { @@ -282,32 +311,70 @@ function AddSquadLabelsToYaml { if ($Lines[$p] -match 'payloadType:\s*Pull_Request') { $isPR = $true; break } } if ($isPR) { - $lastUserEnd = -1 - $usersIndent = $listIndentLength + 4 - $existingUsers = @{} + $lastReviewEnd = -1 + $reviewIndent = $listIndentLength + 4 + $existingReviewers = @{} for ($b = $j; $b -lt $k; $b++) { - if ($Lines[$b] -match '^\s*-\s+assignTo:\s*$' -and (GetIndentLength -Line $Lines[$b]) -eq $listIndentLength) { + if ($Lines[$b] -match '^\s*-\s+requestReview:\s*$' -and (GetIndentLength -Line $Lines[$b]) -eq $listIndentLength) { for ($c = $b + 1; $c -lt $k; $c++) { if ($Lines[$c] -match '^\s*-\s+' -and (GetIndentLength -Line $Lines[$c]) -eq $listIndentLength) { break } - if ($Lines[$c] -match '^\s*-\s+(\S+)\s*$' -and (GetIndentLength -Line $Lines[$c]) -eq $usersIndent) { - $existingUsers[$Matches[1]] = $true - $lastUserEnd = $c + if ($Lines[$c] -match '^\s*reviewer:\s*(\S+)\s*$') { + $existingReviewers[$Matches[1]] = $true + $lastReviewEnd = $c } } } } - if ($lastUserEnd -ge 0) { - $userInsertLines = [System.Collections.Generic.List[string]]::new() + if ($lastReviewEnd -lt 0) { + $lastReviewEnd = $lastAddLabelEnd + } + if ($lastReviewEnd -ge 0) { + $reviewInsertLines = [System.Collections.Generic.List[string]]::new() foreach ($squadLabel in $labelsToAdd) { - if (-not $existingUsers.ContainsKey($squadLabel)) { - $userInsertLines.Add((" " * $usersIndent) + "- $squadLabel") + if (-not $existingReviewers.ContainsKey($squadLabel)) { + $reviewInsertLines.Add((" " * $listIndentLength) + "- requestReview:") + $reviewInsertLines.Add((" " * $reviewIndent) + "reviewer: $squadLabel") } } - if ($userInsertLines.Count -gt 0) { - $insertions.Add([PSCustomObject]@{ Index = $lastUserEnd + 1; Lines = $userInsertLines }) + if ($reviewInsertLines.Count -gt 0) { + $insertions.Add([PSCustomObject]@{ Index = $lastReviewEnd + 1; Lines = $reviewInsertLines }) + } + } + } + + $mentionUsersIndex = -1 + $mentioneesIndent = -1 + $mentionItemIndent = -1 + $existingMentions = @{} + $lastMentionEnd = -1 + for ($b = $j; $b -lt $k; $b++) { + $lineAtB = $Lines[$b] + $indentAtB = GetIndentLength -Line $lineAtB + if ($lineAtB -match '^\s*-\s+mentionUsers:\s*$' -and $indentAtB -eq $listIndentLength) { + $mentionUsersIndex = $b; continue + } + if ($mentionUsersIndex -ge 0) { + if ($lineAtB.Trim().Length -ne 0 -and $indentAtB -le $listIndentLength -and $lineAtB -match '^\s*-\s+') { break } + if ($lineAtB -match '^\s*mentionees:\s*$') { $mentioneesIndent = $indentAtB; continue } + if ($lineAtB -match '^\s*-\s+(\S+)\s*$') { + if ($mentionItemIndent -lt 0) { $mentionItemIndent = $indentAtB } + $existingMentions[$Matches[1]] = $true + $lastMentionEnd = $b } } } + if ($mentionUsersIndex -ge 0 -and $lastMentionEnd -ge 0) { + if ($mentionItemIndent -lt 0) { $mentionItemIndent = ($mentioneesIndent -gt 0) ? $mentioneesIndent : ($listIndentLength + 4) } + $mentionInsertLines = [System.Collections.Generic.List[string]]::new() + foreach ($squadLabel in $labelsToAdd) { + if (-not $existingMentions.ContainsKey($squadLabel)) { + $mentionInsertLines.Add((" " * $mentionItemIndent) + "- $squadLabel") + } + } + if ($mentionInsertLines.Count -gt 0) { + $insertions.Add([PSCustomObject]@{ Index = $lastMentionEnd + 1; Lines = $mentionInsertLines }) + } + } } } } @@ -325,7 +392,6 @@ function AddSquadLabelsToYaml { } return $lineList.ToArray() -} $labelToSquad = GetSquadMappingFromWiki -AccessToken $AccessToken if ($labelToSquad.Count -eq 0) { From 264db8d6c66ab0986f24ece58fc50e39adace50c Mon Sep 17 00:00:00 2001 From: Aditya Pujara <59631311+a0x1ab@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:39:15 +1030 Subject: [PATCH 6/7] fix small errors in indentation --- tools/Github/ParseSquadMappingList.ps1 | 65 ++++++++++++++------------ 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/tools/Github/ParseSquadMappingList.ps1 b/tools/Github/ParseSquadMappingList.ps1 index 780ca129549..22f0908cf76 100644 --- a/tools/Github/ParseSquadMappingList.ps1 +++ b/tools/Github/ParseSquadMappingList.ps1 @@ -286,7 +286,8 @@ function AddSquadLabelsToYaml { } $labelsToAdd = [System.Collections.Generic.List[string]]::new() - foreach ($label in $labelsPresent.Keys) { + $sortedLabels = $labelsPresent.Keys | Sort-Object -CaseSensitive + foreach ($label in $sortedLabels) { if ($LabelToSquad.ContainsKey($label)) { $squadLabel = $LabelToSquad[$label] if (-not $labelsPresent.ContainsKey($squadLabel) -and -not $labelsToAdd.Contains($squadLabel)) { @@ -305,7 +306,7 @@ function AddSquadLabelsToYaml { $insertions.Add([PSCustomObject]@{ Index = $lastAddLabelEnd + 1; Lines = $insertLines }) } - $isPR = $false + $isPR = $false for ($p = $i - 1; $p -ge 0; $p--) { if ($Lines[$p] -match '^\s*description:') { break } if ($Lines[$p] -match 'payloadType:\s*Pull_Request') { $isPR = $true; break } @@ -342,39 +343,40 @@ function AddSquadLabelsToYaml { } } - $mentionUsersIndex = -1 - $mentioneesIndent = -1 - $mentionItemIndent = -1 - $existingMentions = @{} - $lastMentionEnd = -1 - for ($b = $j; $b -lt $k; $b++) { - $lineAtB = $Lines[$b] - $indentAtB = GetIndentLength -Line $lineAtB - if ($lineAtB -match '^\s*-\s+mentionUsers:\s*$' -and $indentAtB -eq $listIndentLength) { - $mentionUsersIndex = $b; continue - } - if ($mentionUsersIndex -ge 0) { - if ($lineAtB.Trim().Length -ne 0 -and $indentAtB -le $listIndentLength -and $lineAtB -match '^\s*-\s+') { break } - if ($lineAtB -match '^\s*mentionees:\s*$') { $mentioneesIndent = $indentAtB; continue } - if ($lineAtB -match '^\s*-\s+(\S+)\s*$') { - if ($mentionItemIndent -lt 0) { $mentionItemIndent = $indentAtB } - $existingMentions[$Matches[1]] = $true - $lastMentionEnd = $b - } - } + $mentionUsersIndex = -1 + $mentioneesIndent = -1 + $mentionItemIndent = -1 + $existingMentions = @{} + $lastMentionEnd = -1 + for ($b = $j; $b -lt $k; $b++) { + $lineAtB = $Lines[$b] + $indentAtB = GetIndentLength -Line $lineAtB + if ($lineAtB -match '^\s*-\s+mentionUsers:\s*$' -and $indentAtB -eq $listIndentLength) { + $mentionUsersIndex = $b + continue } - if ($mentionUsersIndex -ge 0 -and $lastMentionEnd -ge 0) { - if ($mentionItemIndent -lt 0) { $mentionItemIndent = ($mentioneesIndent -gt 0) ? $mentioneesIndent : ($listIndentLength + 4) } - $mentionInsertLines = [System.Collections.Generic.List[string]]::new() - foreach ($squadLabel in $labelsToAdd) { - if (-not $existingMentions.ContainsKey($squadLabel)) { - $mentionInsertLines.Add((" " * $mentionItemIndent) + "- $squadLabel") - } + if ($mentionUsersIndex -ge 0) { + if ($lineAtB.Trim().Length -ne 0 -and $indentAtB -le $listIndentLength -and $lineAtB -match '^\s*-\s+') { break } + if ($lineAtB -match '^\s*mentionees:\s*$') { $mentioneesIndent = $indentAtB; continue } + if ($lineAtB -match '^\s*-\s+(\S+)\s*$') { + if ($mentionItemIndent -lt 0) { $mentionItemIndent = $indentAtB } + $existingMentions[$Matches[1]] = $true + $lastMentionEnd = $b } - if ($mentionInsertLines.Count -gt 0) { - $insertions.Add([PSCustomObject]@{ Index = $lastMentionEnd + 1; Lines = $mentionInsertLines }) + } + } + + if ($mentionUsersIndex -ge 0 -and $lastMentionEnd -ge 0) { + if ($mentionItemIndent -lt 0) { $mentionItemIndent = ($mentioneesIndent -gt 0) ? $mentioneesIndent : ($listIndentLength + 4) } + $mentionInsertLines = [System.Collections.Generic.List[string]]::new() + foreach ($squadLabel in $labelsToAdd) { + if (-not $existingMentions.ContainsKey($squadLabel)) { + $mentionInsertLines.Add((" " * $mentionItemIndent) + "- $squadLabel") } } + if ($mentionInsertLines.Count -gt 0) { + $insertions.Add([PSCustomObject]@{ Index = $lastMentionEnd + 1; Lines = $mentionInsertLines }) + } } } } @@ -392,6 +394,7 @@ function AddSquadLabelsToYaml { } return $lineList.ToArray() +} $labelToSquad = GetSquadMappingFromWiki -AccessToken $AccessToken if ($labelToSquad.Count -eq 0) { From 9980b84510b556d1239f97fb11f7be24bd9ffea3 Mon Sep 17 00:00:00 2001 From: Aditya Pujara <59631311+a0x1ab@users.noreply.github.com> Date: Fri, 27 Mar 2026 11:54:59 +1030 Subject: [PATCH 7/7] Added new rules to resourceManagemnt.yml --- .github/policies/resourceManagement.yml | 90 +++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/.github/policies/resourceManagement.yml b/.github/policies/resourceManagement.yml index 37c39eae7c8..4b6e9ce9a4b 100644 --- a/.github/policies/resourceManagement.yml +++ b/.github/policies/resourceManagement.yml @@ -109,6 +109,96 @@ configuration: - removeLabel: label: no-recent-activity description: '[Resolve Workflow] Issue Addressed Label Applied' + - if: + - payloadType: Pull_Request + - isAction: + action: Labeled + - hasLabel: + label: act-identity-squad + then: + - requestReview: + reviewer: act-identity-squad + - mentionUsers: + mentionees: + - act-identity-squad + replyTemplate: ":bell: Routing this PR to ${mentionees}." + assignMentionees: False + description: '[Squad Auto-Assign] Request review & mention when PR labeled act-identity-squad' + - if: + - payloadType: Pull_Request + - isAction: + action: Labeled + - hasLabel: + label: act-observability-squad + then: + - requestReview: + reviewer: act-observability-squad + - mentionUsers: + mentionees: + - act-observability-squad + replyTemplate: ":bell: Routing this PR to ${mentionees}." + assignMentionees: False + description: '[Squad Auto-Assign] Request review & mention when PR labeled act-observability-squad' + - if: + - payloadType: Pull_Request + - isAction: + action: Labeled + - hasLabel: + label: act-codegen-extensibility-squad + then: + - requestReview: + reviewer: act-codegen-extensibility-squad + - mentionUsers: + mentionees: + - act-codegen-extensibility-squad + replyTemplate: ":bell: Routing this PR to ${mentionees}." + assignMentionees: False + description: '[Squad Auto-Assign] Request review & mention when PR labeled act-codegen-extensibility-squad' + - if: + - payloadType: Pull_Request + - isAction: + action: Labeled + - hasLabel: + label: act-quality-productivity-squad + then: + - requestReview: + reviewer: act-quality-productivity-squad + - mentionUsers: + mentionees: + - act-quality-productivity-squad + replyTemplate: ":bell: Routing this PR to ${mentionees}." + assignMentionees: False + description: '[Squad Auto-Assign] Request review & mention when PR labeled act-quality-productivity-squad' + - if: + - payloadType: Pull_Request + - isAction: + action: Labeled + - hasLabel: + label: act-platform-engineering-squad + then: + - requestReview: + reviewer: act-platform-engineering-squad + - mentionUsers: + mentionees: + - act-platform-engineering-squad + replyTemplate: ":bell: Routing this PR to ${mentionees}." + assignMentionees: False + description: '[Squad Auto-Assign] Request review & mention when PR labeled act-platform-engineering-squad' + - if: + - payloadType: Pull_Request + - isAction: + action: Labeled + - hasLabel: + label: act-experience-enablement-squad + then: + - requestReview: + reviewer: act-experience-enablement-squad + - mentionUsers: + mentionees: + - act-experience-enablement-squad + replyTemplate: ":bell: Routing this PR to ${mentionees}." + assignMentionees: False + description: '[Squad Auto-Assign] Request review & mention when PR labeled act-experience-enablement-squad' - if: - payloadType: Issue_Comment - hasLabel: