Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ stages:
# condition: dependencies.AutoReleasePrepare.outputs['ResolveAutoReleasePackages.resolve.<var>']
# variables: stageDependencies.AutoReleasePrepare.ResolveAutoReleasePackages.outputs['resolve.<var>']
# Emitted variables: HasAutoReleaseArtifacts (the single eligibility gate), AutoReleaseArtifactsJson,
# and ReleaseArtifact_<safeName> per declared artifact.
# ReleaseArtifact_<safeName> per declared artifact, and AutoReleaseSdkPullRequestUrl for completion.
- stage: AutoReleasePrepare
displayName: Auto-release prepare
dependsOn: ${{ parameters.DependsOn }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ parameters:
ConfigFileDir: ''
PackageArtifactName: ''
SourceRootPath: $(Build.SourcesDirectory)
ReleasePlanId: 0
SdkPullRequest: ''

steps:
- template: /eng/common/pipelines/templates/steps/install-azsdk-cli.yml
Expand All @@ -16,7 +18,10 @@ steps:
scriptPath: ${{ parameters.SourceRootPath }}/eng/common/scripts/Mark-ReleasePlanCompletion.ps1
arguments: >
-PackageInfoFilePath '${{ parameters.ConfigFileDir }}/${{ parameters.PackageArtifactName }}.json' -AzsdkExePath '$(AZSDK)'
-ReleasePlanId '${{ parameters.ReleasePlanId }}'
-SdkPullRequest '${{ parameters.SdkPullRequest }}'
-PipelineUrl "$(System.CollectionUri)$(System.TeamProject)/_build/results?buildId=$(Build.BuildId)"
workingDirectory: $(Pipeline.Workspace)
displayName: Mark package as released
continueOnError: true
condition: and(succeeded(), ne(variables['Skip.MarkReleaseCompletion'], 'true'))
condition: and(succeeded(), ne(variables['Skip.MarkReleaseCompletion'], 'true'))
113 changes: 84 additions & 29 deletions eng/common/scripts/Mark-ReleasePlanCompletion.ps1
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
param(
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$PackageInfoFilePath,
[Parameter(Mandatory = $true)]
[string]$AzsdkExePath
[string]$AzsdkExePath,
[ValidateScript({
$id = 0
if (-not [int]::TryParse($_, [System.Globalization.NumberStyles]::None, [System.Globalization.CultureInfo]::InvariantCulture, [ref]$id))
{
throw 'Release plan ID must be a nonnegative 32-bit integer, without fractions or whitespace.'
}
return $true
})]
[string]$ReleasePlanId = '0',
[string]$SdkPullRequest = '',
[string]$PipelineUrl = ''
)

<#
.SYNOPSIS
Marks release plan completion by identifying pull requests that changed files in a given path.
Updates release status using an explicit release-plan ID or SDK PR when supplied, otherwise the legacy package lookup.

.DESCRIPTION
This script helps to mark release plan completion by finding the active release plans for a package name
Uses the requester-supplied release-plan ID for manual releases, or the triggering SDK PR
for automatic releases. azsdk resolves the existing ADO plan and validates the language/package.
TRANSITIONAL: when neither is supplied, the original package-name lookup is used and an informational message is logged
so pipelines that do not yet forward the association keep working. Remove once every pipeline forwards one.

.PARAMETER PackageInfoFilePath
The path to the package information file (required) or path to the directory containing package information files.

.PARAMETER AzsdkExePath
The path to the azsdk executable used to mark the release completion.

.PARAMETER ReleasePlanId
The release-plan ID explicitly supplied for this manual release. Zero means no manual association.

.PARAMETER SdkPullRequest
The unambiguous SDK PR that triggered the automatic release. Not a spec PR or a previous package PR.

.PARAMETER PipelineUrl
The current release pipeline URL, recorded for manual and automatic releases.
#>

Set-StrictMode -Version 3
Set-StrictMode -Version 4
$ErrorActionPreference = 'Stop'
[int]$suppliedPlanId = [int]::Parse($ReleasePlanId, [System.Globalization.CultureInfo]::InvariantCulture)
. (Join-Path $PSScriptRoot common.ps1)

#Validate azsdk executable path
Expand All @@ -39,50 +65,79 @@ if (-Not (Test-Path $PackageInfoFilePath))
function Process-Package([string]$packageInfoPath)
{
# Get package info from json file created before updating version to daily dev
$pkgInfo = Get-Content $packageInfoPath | ConvertFrom-Json
$PackageName = $pkgInfo.Name
if (!$PackageName)
$pkgInfo = Get-Content -LiteralPath $packageInfoPath -Raw | ConvertFrom-Json -AsHashtable
if ($pkgInfo -isnot [System.Collections.IDictionary])
{
Write-Warning "Package information must be a JSON object: $packageInfoPath. No release plan was updated."
return
}
$PackageName = $pkgInfo['Name']
if ($PackageName -isnot [string] -or [string]::IsNullOrWhiteSpace($PackageName))
{
Write-Host "Package name is not available in the package information file. Skipping the release plan status update for the package."
return
}
}

Write-Host "Marking release completion for package, name: $PackageName"
$PackageVersion = $pkgInfo.Version
$version = [AzureEngSemanticVersion]::ParseVersionString($PackageVersion)
if (!$version)
$isLegacyLookup = ($suppliedPlanId -eq 0 -and [string]::IsNullOrWhiteSpace($SdkPullRequest))
if ($isLegacyLookup)
{
Write-Host "Failed to parse version string '$($PackageVersion)' for package '$PackageName'. Skipping the release plan status update."
return
Write-Host "LEGACY_RELEASE_PLAN_LOOKUP: Package '$PackageName' has no release-plan ID or triggering SDK PR; using the legacy package lookup. Forward ReleasePlanId or SdkPullRequest from the release pipeline."
}

$sdkReleaseType = ""
if ($version.IsPrerelease)
# Do not inherit plan IDs from package metadata; unrelated bug-fix builds can reuse those files.
Write-Host "Correlating release status for package '$PackageName', language '$LanguageDisplayName'."
$releaseArgs = @("release-plan", "update-release-status", "--package-name", $PackageName, "--language", $LanguageDisplayName, "--status", "Released")
if ($suppliedPlanId -gt 0)
{
$sdkReleaseType = "beta"
$releaseArgs += @("--release-plan-id", "$suppliedPlanId")
}
else
if (-not [string]::IsNullOrWhiteSpace($SdkPullRequest))
{
$sdkReleaseType = "stable"
$releaseArgs += @("--sdk-pull-request", $SdkPullRequest)
}

$releaseArgs = @("release-plan", "update-release-status", "--package-name", $PackageName, "--language", $LanguageDisplayName, "--status", "Released", "--sdk-release-type", $sdkReleaseType)
if ($PackageVersion)
if (-not [string]::IsNullOrWhiteSpace($PipelineUrl))
{
$releaseArgs += @("--release-pipeline", $PipelineUrl)
}
$PackageVersion = $pkgInfo['Version']
$version = $null
if ($PackageVersion -is [string] -and -not [string]::IsNullOrWhiteSpace($PackageVersion))
{
$releaseArgs += @("--package-version", $PackageVersion)
$version = [AzureEngSemanticVersion]::ParseVersionString($PackageVersion)
}
if ($version)
{
$sdkReleaseType = if ($version.IsPrerelease) { 'beta' } else { 'stable' }
$releaseArgs += @("--package-version", $PackageVersion, "--sdk-release-type", $sdkReleaseType)
}
else
{
Write-Host "Package version is missing or invalid for '$PackageName'. Updating release status without version metadata."
}
$releaseInfo = & $AzsdkExePath @releaseArgs
if ($LASTEXITCODE -ne 0)
{
## Not all releases have a release plan. So we should not fail the script even if a release plan is missing.
Write-Host "Failed to mark release completion for package '$PackageName' using azsdk. Exit code: $LASTEXITCODE"
Write-Host "Release plan status was not updated for package '$PackageName'. Exit code: $LASTEXITCODE. Not all packages have a release plan."
}
Write-Host "Details: $releaseInfo"
return
}

Write-Host "Finding all package info files in the path: $PackageInfoFilePath"
# Get all package info file under the directory given in input param and process
Get-ChildItem -Path $PackageInfoFilePath -Filter "*.json" | ForEach-Object {
Write-Host "Processing package info file: $_"
Process-Package $_.FullName
}
foreach ($packageInfoFile in Get-ChildItem -Path $PackageInfoFilePath -Filter "*.json" -File)
{
try
{
Write-Host "Processing package info file: $packageInfoFile"
Process-Package $packageInfoFile.FullName
}
catch
{
Write-Warning "Failed to update release status for '$packageInfoFile': $($_.Exception.Message)"
}
}

# Status reporting must not change the outcome of an already completed package release.
exit 0
9 changes: 6 additions & 3 deletions eng/common/scripts/Resolve-AutoReleasePackages.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ The path to the azsdk executable used for release operations. Defaults to the AZ
Azure DevOps output variables (reference cross-stage via dependencies.<stage>.outputs['<job>.<step>.<name>']):
- HasAutoReleaseArtifacts : 'true' if at least one declared package is releasable
- AutoReleaseArtifactsJson : JSON array of the matched declared-artifact objects (or '[]')
- AutoReleaseSdkPullRequestUrl : the SDK PR selected for this release, for release-stage status correlation
- ReleaseArtifact_<safeName> : 'true'/'false' per declared artifact
HasAutoReleaseArtifacts is the single eligibility gate: it is 'true' only when a merged, auto-release-labeled
PR changed at least one declared package, and it is emitted last so any earlier failure fails closed.
Expand Down Expand Up @@ -101,6 +102,7 @@ catch {
# Fail-closed defaults: nothing releases unless we positively determine otherwise below.
Set-PipelineVariable -Name 'HasAutoReleaseArtifacts' -Value 'false' -IsOutput
Set-PipelineVariable -Name 'AutoReleaseArtifactsJson' -Value '[]' -IsOutput
Set-PipelineVariable -Name 'AutoReleaseSdkPullRequestUrl' -Value '' -IsOutput
foreach ($artifact in $declaredArtifacts) {
if ($artifact.PSObject.Properties['safeName'] -and $artifact.safeName) {
Set-PipelineVariable -Name "ReleaseArtifact_$($artifact.safeName)" -Value 'false' -IsOutput
Expand Down Expand Up @@ -238,9 +240,8 @@ function Invoke-AutoReleaseResolution {
}
if($AzsdkExePath)
{
$sdkPullRequestUrl = $pr.html_url
Write-Host "Updating release plan for package '$packageName' (artifact name: '$name', package name: '$packageName')"
$cliArgs = @("release-plan", "update-release-status", "--package-name", $packageName, "--language", $LanguageDisplayName, "--status", "Release In Progress", "--sdk-pull-request", $sdkPullRequestUrl)
$cliArgs = @("release-plan", "update-release-status", "--package-name", $packageName, "--language", $LanguageDisplayName, "--status", "Release In Progress", "--sdk-pull-request", $prLink)
if ($PipelineUrl)
{
$cliArgs += @("--release-pipeline", $PipelineUrl)
Expand All @@ -254,7 +255,7 @@ function Invoke-AutoReleaseResolution {
if ($LASTEXITCODE -ne 0)
{
## Not all releases have a release plan. So we should not fail the script even if a release plan is missing.
Write-Host "Failed to update release in progress status for package '$packageName' using azsdk. Exit code: $LASTEXITCODE"
LogWarning "Failed to update release in progress status for package '$packageName' using azsdk. Exit code: $LASTEXITCODE"
}
}
else
Expand All @@ -280,6 +281,7 @@ function Invoke-AutoReleaseResolution {
# Pipe (not -InputObject) with -AsArray so a single match still serializes as a JSON array, '[{...}]'.
$artifactsJson = $matchedArtifacts | ConvertTo-Json -Depth 100 -Compress -AsArray
Set-PipelineVariable -Name 'AutoReleaseArtifactsJson' -Value $artifactsJson -IsOutput
Set-PipelineVariable -Name 'AutoReleaseSdkPullRequestUrl' -Value $prLink -IsOutput
Write-Host "Auto-release packages from PR ${prLink}: $((@($matchedArtifacts | ForEach-Object { $_.name })) -join ', ')"
Set-PipelineVariable -Name 'HasAutoReleaseArtifacts' -Value 'true' -IsOutput
}
Expand All @@ -298,6 +300,7 @@ catch {
LogWarning "Auto-release resolution failed; skipping auto-release. $($_.Exception.Message)"
Set-PipelineVariable -Name 'HasAutoReleaseArtifacts' -Value 'false' -IsOutput
Set-PipelineVariable -Name 'AutoReleaseArtifactsJson' -Value '[]' -IsOutput
Set-PipelineVariable -Name 'AutoReleaseSdkPullRequestUrl' -Value '' -IsOutput
foreach ($artifact in $declaredArtifacts) {
if ($artifact.PSObject.Properties['safeName'] -and $artifact.safeName) {
Set-PipelineVariable -Name "ReleaseArtifact_$($artifact.safeName)" -Value 'false' -IsOutput
Expand Down