Skip to content
Merged
29 changes: 29 additions & 0 deletions eng/pipelines/report-unreleased-sdks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
trigger: none
pr: none

pool:
name: azsdk-pool
demands: ImageOverride -equals ubuntu-24.04

jobs:
- job: ReportUnreleasedSdks
steps:
- checkout: self

- task: PowerShell@2
displayName: 'Install Azure SDK MCP'
inputs:
targetType: 'inline'
script: './eng/common/mcp/azure-sdk-mcp.ps1 -InstallDirectory $(System.DefaultWorkingDirectory)'
pwsh: true
workingDirectory: '$(System.DefaultWorkingDirectory)'

- task: AzureCLI@2
displayName: Email product owners about overdue SDK release plans
inputs:
azureSubscription: opensource-api-connection
scriptType: pscore
scriptPath: eng/scripts/Report-Unreleased-Sdks.ps1
arguments: >
-AzureSDKEmailUri "$(AzureSDKEmailerSasURL)"
-AzsdkExePath "$(System.DefaultWorkingDirectory)/azsdk"
102 changes: 102 additions & 0 deletions eng/scripts/Report-Unreleased-Sdks.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<#
.SYNOPSIS
Automates email notifications to Azure SDK release plan owners when Tier 1 SDK coverage is missing.

.DESCRIPTION
This script checks overdue Azure SDK release plans for missing Tier 1 languages. For any gaps, it builds an HTML email and sends it to the plan owner with steps to fix the issue (generate SDKs or request an exception).

.PARAMETER AzureSDKEmailUri
The Uri of the app used to send email notifications

.PARAMETER AzsdkExePath
Path to the Azure SDK CLI executable (azsdk).
#>
param (
[Parameter(Mandatory = $true)]
[string] $AzureSDKEmailUri,

[Parameter(Mandatory = $true)]
[string] $AzsdkExePath
)

Set-StrictMode -Version 3

$AZURE_SDK_APEX_EMAIL = "[email protected]"
$SUBJECT = "Action Required: Missing Tier 1 language in your Azure SDK Release Plan"

function BuildEmailNotification($releaseOwnerName, $plane, $missingSDKs, $releasePlanLink) {
$body = @"
<html>
<body>
<p>Hello $releaseOwnerName,</p>
<p>Our automation has flagged your Azure SDK release plan as missing one or more Tier 1 SDKs for the following plane:</p>
<ul>
<li><strong>Plane:</strong> $plane</li>
<li><strong>Missing SDKs:</strong> $missingSDKs</li>
<li><strong>Release Plan Link:</strong> <a href=$releasePlanLink>$releasePlanLink</a></li>
</ul>
<p>Per Azure SDK release requirements, all Tier 1 languages must be supported unless an approved exclusion is filed. Please take one of the following actions:</p>
<ol>
<li>Generate and release the missing SDKs using <a href='https://aka.ms/azsdk/dpcodegen'>https://aka.ms/azsdk/dpcodegen</a></li>
<li>File for an exclusion: <a href='https://eng.ms/docs/products/azure-developer-experience/onboard/request-exception'>https://eng.ms/docs/products/azure-developer-experience/onboard/request-exception</a></li>
</ol>
<p>Thank you for helping maintain language parity across Azure SDKs.</p>
<p>Best regards,<br/>Azure SDK PM Team</p>
</body>
</html>
"@
return $body
}

if (-not (Test-Path $AzsdkExePath)) {
throw "Azure SDK CLI executable not found at: $AzsdkExePath"
}

$jsonOutput = & $AzsdkExePath release-plan list-overdue --output json
$releasePlansData = $jsonOutput | ConvertFrom-Json
$releasePlans = $releasePlansData.release_plans

if (-not $releasePlans) {
throw "Unexpected JSON structure. Expected 'release_plans' property."
}


foreach ($releasePlan in $releasePlans) {
$releaseOwnerEmail = $releasePlan.ReleasePlanSubmittedByEmail

try {
# Throws on invalid emails
[void][System.Net.Mail.MailAddress]::new($releaseOwnerEmail)
}
catch {
Write-Host ("Skipped notification for Release Plan ID {0}: invalid email '{1}'" -f $releasePlan.id, $releaseOwnerEmail)
continue
}

$releaseOwnerName = $releasePlan.Owner
$plane = $releasePlan.IsManagementPlane ? "Management Plane" : "Data Plane"
$releasePlanLink = $releasePlan.ReleasePlanLink

# Start with all Tier 1 languages
$missingSDKs = @('.NET', 'JavaScript', 'Python', 'Java', 'Go')

# Skip Go for Data Plane release plans
if ($releasePlan.IsDataPlane) {
$missingSDKs = $missingSDKs | Where-Object { $_ -ine 'Go' }
}

foreach ($info in $releasePlan.SDKInfo) {
$statusNorm = $info.ReleaseStatus.ToLower()

# Remove language from missing list if it's released
$isReleased = ($statusNorm -eq 'released')
if ($isReleased) {
$missingSDKs = $missingSDKs | Where-Object { $_ -ine $info.Language }
}
}

$missingSDKsString = ($missingSDKs -join ', ')
$body = BuildEmailNotification -releaseOwnerName $releaseOwnerName -plane $plane -missingSDKs $missingSDKsString -releasePlanLink $releasePlanLink

& (Join-Path $PSScriptRoot "Send-Email-Notification.ps1") -AzureSDKEmailUri $AzureSDKEmailUri -To $releaseOwnerEmail -CC $AZURE_SDK_APEX_EMAIL -Subject $SUBJECT -Body $body
}
51 changes: 51 additions & 0 deletions eng/scripts/Send-Email-Notification.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<#
.SYNOPSIS
Sends an email notification using a specified Azure SDK email service endpoint.

.DESCRIPTION
This script posts an email payload to the Azure SDK email service via a REST API call.
It accepts recipient details, subject, and body content, converts them to JSON, and sends
the request to the provided URI.

.PARAMETER AzureSDKEmailUri
The Uri of the app used to send email notifications

.PARAMETER To
The primary recipient email address(es).

.PARAMETER Cc
Optional CC recipient email address(es).

.PARAMETER Subject
The subject line of the email.

.PARAMETER Body
The HTML or plain text body of the email message.
#>
param (
[Parameter(Mandatory = $true)]
[string] $AzureSDKEmailUri,

[Parameter(Mandatory = $true)]
[string] $To,

[Parameter(Mandatory = $false)]
[string] $CC,

[Parameter(Mandatory = $true)]
[string] $Subject,

[Parameter(Mandatory = $true)]
[string] $Body
)

Set-StrictMode -Version 3

try {
$requestBody = @{ EmailTo = $To; CC = $CC; Subject = $Subject; Body = $Body} | ConvertTo-Json -Depth 3
Write-Host "Sending Email - To: $To`nCC: $CC`nSubject: $Subject`nBody: $Body"
$response = Invoke-RestMethod -Uri $AzureSDKEmailUri -Method Post -Body $requestBody -ContentType "application/json"
Write-Host "Successfully Sent Email - To: $To`nCC: $CC`nSubject: $Subject`nBody: $Body"
} catch {
Write-Error "Failed to send email.`nTo: $To`nCC: $CC`nSubject: $Subject`nBody: $Body`nException message: $($_.Exception.Message)"
}