From edf7c80f0550af80800f71d1b2cf7bbbca1217ef Mon Sep 17 00:00:00 2001 From: David Strome <21028455+dstrome@users.noreply.github.com> Date: Wed, 12 Nov 2025 16:39:50 -0800 Subject: [PATCH 1/3] Add support for docfx author, ms.service, ms.subservice --- .github/workflows/Shared-AutoLabelAssign.yml | 258 +++++++++++++++++-- 1 file changed, 242 insertions(+), 16 deletions(-) diff --git a/.github/workflows/Shared-AutoLabelAssign.yml b/.github/workflows/Shared-AutoLabelAssign.yml index 8e9a2b74a75..6609132be43 100644 --- a/.github/workflows/Shared-AutoLabelAssign.yml +++ b/.github/workflows/Shared-AutoLabelAssign.yml @@ -39,7 +39,10 @@ jobs: if: github.repository_owner == 'MicrosoftDocs' && contains(github.event.repository.topics, 'build') runs-on: ubuntu-latest steps: - + - name: Check out repo + id: checkout-repo + uses: actions/checkout@v4 + - name: Create App Token id: app-token uses: actions/create-github-app-token@v1 @@ -76,7 +79,7 @@ jobs: run: | # Get runspace info - $RepoRoot = $env:RUNNER_WORKSPACE + $RepoRoot = $env:GITHUB_WORKSPACE $RepoName = $env:GITHUB_REPOSITORY $WorkflowName = $env:GITHUB_WORKFLOW -replace '[\\/:*?"<>|\s]', '_' $WorkflowRunId = $env:GITHUB_RUN_ID @@ -175,6 +178,129 @@ jobs: Write-Host "Repository URL: $RepoUrl" + ##################### + ##################### + # Convert-DocFxMetadataPatternToRegex + + Function Convert-DocFxMetadataPatternToRegex { + Param ( + [string]$Pattern + ) + + $Clean = ($Pattern -replace '\\', '/') -replace '^\./', '' + $Regex = [Regex]::Escape($Clean) -replace '/\\\*\\\*/', '/(?:[^/]+/)*' ` + -replace '/\\\*\\\*$', '/.*' ` + -replace '\\\*\\\*', '.*' ` + -replace '\\\*', '[^/]*' ` + -replace '\\\?', '[^/]' + + # Both absolute (starts with /) and relative patterns should anchor to repo root + Return $Clean.StartsWith('/') ? "^$($Regex.TrimStart('/'))$" : "^$Regex$" + + } + + ##################### + ##################### + # Get-DocFxFileMetadataFromPattern + + Function Get-DocFxFileMetadataFromPattern { + + [CmdletBinding()] + Param ( + [object]$PatternTable, + + [Parameter(Mandatory = $true)] + [string]$FilePath + ) + + If ($PatternTable -ne $Null) { + + If ($PatternTable -isnot [hashtable]) { + + $Ordered = [ordered]@{} + $PatternTable.PSObject.Properties | ForEach-Object { $Ordered[$_.Name] = $_.Value } + $PatternTable = $Ordered + + } + + $NormPath = ($FilePath -replace '\\', '/') -replace '^\./', '' + + Foreach ($Pattern in $PatternTable.Keys) { + + If ($NormPath -match (Convert-DocFxMetadataPatternToRegex -Pattern $Pattern)) { + + Return $PatternTable[$Pattern] + + } + + } + + } Else { + + Write-Host "No data for specified attribute found in DocFx fileMetadata config." + + } + + Return $null + + } + + ##################### + ##################### + # Get-DocFxConfig + + Function Get-DocFxConfig { + + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $true)] + [string]$FilePath + ) + + If (-not $RepoRoot) { + + Throw 'Repository root could not be determined.' + + } + + $FullPath = (Resolve-Path -LiteralPath $FilePath -ErrorAction Stop).Path + $RepoRoot = (Resolve-Path -LiteralPath $RepoRoot -ErrorAction Stop).Path.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) + + # Confirm the file is inside the repo + If (-not $FullPath.StartsWith($RepoRoot, [StringComparison]::OrdinalIgnoreCase)) { + + Throw 'FilePath is not located underneath the repository root.' + + } + + # Walk up the tree + $CurrentDir = Split-Path -Path $FullPath -Parent + + While ($CurrentDir) { + + $Candidate = Join-Path -Path $CurrentDir -ChildPath 'docfx.json' + + If (Test-Path -LiteralPath $Candidate -PathType Leaf) { + + Write-Host "Getting DocFx config from $Candidate" + + Return (Get-Content -LiteralPath $Candidate -Raw) | ConvertFrom-Json -AsHashtable + } + + If ($CurrentDir.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) -eq $RepoRoot) { + + Break + + } + + # Get the parent of the current folder + $CurrentDir = Split-Path -Path $CurrentDir -Parent + + } + + Return $null + } + ##################### ##################### # Get-FileMetadata @@ -209,32 +335,130 @@ jobs: $FileContentsBase64 = Invoke-RestMethod -Method GET -Uri $File.contents_url -Headers $GitHubHeaders $FileContents = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($FileContentsBase64.content)) - # Check to see if the file contents contains a string that matches the $AuthorRegex regex pattern. If yes, add value to $Author, if not assign $Null. + # Retrieve DocFx configuration that applies to current file. Retrieving for each file is inefficient but most PRs only have a few files in them. + # Pre-processing to be more efficient could actually use more cycles. + $DocFxConfig = Get-DocFxConfig -FilePath $FileName + + # Check to see if the file contents contains a string that matches the $AuthorRegex regex pattern. If yes, add value to $Author, check + # fileMetadata and globalMetadata in that order in DocFx. If there's a match in either of those, return value. If not, return null. If ($FileContents -match $AuthorRegex) { + $Author = $Matches[2] $MetadataFound = $True + Write-Host "Found author $Author." + } Else { - $Author = $Null + + Write-Host "Author not found in article. Checking DocFx fileMetadata for $FileName." + + $Author = Get-DocFxFileMetadataFromPattern -PatternTable $DocFxConfig.build.fileMetadata.author -FilePath $FileName + + If ($Author -ne $Null) { + + Write-Host "Author $Author found in DocFx fileMetadata." + $MetadataFound = $True + + } Else { + + $Author = $DocFxConfig.build.globalMetadata.author + + If ($Author -ne $Null) { + + Write-Host "Author $Author found in DocFx globalMetadata." + $MetadataFound = $True + + } Else { + + Write-Host "Author not found in DocFx globalMetadata. Returning null." + $Author = $Null + + } + + } + } - # Check to see if file contents contains a string that matches the $ServiceRegex regex pattern. If yes, add value to $Service. Then check SubService regex pattern. - # If value isn't matched, assign $Null and don't check SubService. - If ($FileContents -match $ServiceRegex) { + # Check to see if file contents contains a string that matches the $ServiceRegex regex pattern. If yes, add value to $Service, check + # fileMetadata and globalMetadata in that order in DocFx. If there's a match in either of those, return value. If not, return null. + + If ($FileContents -match $ServiceRegex) { + $Service = $Matches[2] $MetadataFound = $True + Write-Host "Found service $Service." - If ($FileContents -match $SubServiceRegex) - { - $SubService = $Matches[2] - $MetadataFound = $True - Write-Host "Found sub service $SubService." - } Else { - $SubService = $Null - } + } Else { - $Service = $Null + + Write-Host "Service not found in article. Checking DocFx fileMetadata for $FileName." + + $Service = Get-DocFxFileMetadataFromPattern -PatternTable $DocFxConfig.build.fileMetadata.'ms.service' -FilePath $FileName + + If ($Service -ne $Null) { + + Write-Host "Service $Service found in DocFx fileMetadata." + $MetadataFound = $True + + + } Else { + + $Service = $DocFxConfig.build.globalMetadata.'ms.service' + + If ($Service -ne $Null) { + + Write-Host "Service $Service found in DocFx globalMetadata." + $MetadataFound = $True + + } Else { + + Write-Host "Service not found in DocFx globalMetadata. Returning null." + $Service = $Null + + } + + } + } + + # Check to see if file contents contains a string that matches the $SubServiceRegex regex pattern. If yes, add value to $SubService, check + # fileMetadata and globalMetadata in that order in DocFx. If there's a match in either of those, return value. If not, return null. + If ($FileContents -match $SubServiceRegex) { + + $SubService = $Matches[2] + $MetadataFound = $True + + Write-Host "Found sub service $SubService." + + } Else { + + Write-Host "SubService not found in article. Checking DocFx fileMetadata for $FileName." + + $SubService = Get-DocFxFileMetadataFromPattern -PatternTable $DocFxConfig.build.fileMetadata.'ms.subservice' -FilePath $FileName + + If ($SubService -ne $Null) { + + Write-Host "SubService $SubService found in DocFx fileMetadata." + + } Else { + + $SubService = $DocFxConfig.build.globalMetadata.'ms.subservice' + + If ($SubService -ne $Null) { + + Write-Host "SubService $SubService found in DocFx globalMetadata." + + } Else { + + Write-Host "SubService not found in DocFx globalMetadata. Returning null." + $SubService = $Null + + } + + } + + } + # Check to see if file contents contains a string that matches the $ProdRegex regex pattern. If yes, add value to $Product. Then check TechnologyRegex regex pattern. # If value isn't matched, assign $Null and don't check Technology. If ($FileContents -match $ProdRegex) { @@ -1060,6 +1284,7 @@ jobs: } + ##################### ##################### # Main @@ -1338,3 +1563,4 @@ jobs: Write-Host "Event action not ready_for_review, opened, reopened, or synchronize." } # PR event and action check + From b5ea40bdf04f6d5a82c149f397ed1cadd5ef3428 Mon Sep 17 00:00:00 2001 From: David Strome <21028455+dstrome@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:35:50 -0800 Subject: [PATCH 2/3] Update actions/checkout to target the head repo/branch --- .github/workflows/Shared-AutoLabelAssign.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/Shared-AutoLabelAssign.yml b/.github/workflows/Shared-AutoLabelAssign.yml index 6609132be43..5e5a6b57eb7 100644 --- a/.github/workflows/Shared-AutoLabelAssign.yml +++ b/.github/workflows/Shared-AutoLabelAssign.yml @@ -42,6 +42,10 @@ jobs: - name: Check out repo id: checkout-repo uses: actions/checkout@v4 + with: + repository: ${{ fromJson(inputs.PayloadJson).event.pull_request.head.repo.full_name }} + ref: ${{ fromJson(inputs.PayloadJson).event.pull_request.head.ref }} + fetch-depth: 0 - name: Create App Token id: app-token From 5cbe61225367d4e49c44efbefd949827590d36dd Mon Sep 17 00:00:00 2001 From: David Strome <21028455+dstrome@users.noreply.github.com> Date: Thu, 13 Nov 2025 16:30:34 -0800 Subject: [PATCH 3/3] Fix checkout target --- .github/workflows/Shared-AutoLabelAssign.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/Shared-AutoLabelAssign.yml b/.github/workflows/Shared-AutoLabelAssign.yml index 5e5a6b57eb7..1fdfd17abd9 100644 --- a/.github/workflows/Shared-AutoLabelAssign.yml +++ b/.github/workflows/Shared-AutoLabelAssign.yml @@ -43,9 +43,7 @@ jobs: id: checkout-repo uses: actions/checkout@v4 with: - repository: ${{ fromJson(inputs.PayloadJson).event.pull_request.head.repo.full_name }} - ref: ${{ fromJson(inputs.PayloadJson).event.pull_request.head.ref }} - fetch-depth: 0 + ref: ${{ fromJson(inputs.PayloadJson).event.pull_request.head.sha }} - name: Create App Token id: app-token