[CmdletBinding()] Param ( [Parameter(Mandatory=$False)] [array]$ArtifactList, [Parameter(Mandatory=$True)] [string]$ArtifactPath, [Parameter(Mandatory=$True)] [string]$RepoRoot, [Parameter(Mandatory=$True)] [string]$APIKey, [string]$ConfigFileDir, [string]$BuildDefinition, [string]$PipelineUrl, [string]$APIViewUri = "https://apiview.dev/AutoReview/GetReviewStatus", [bool] $IsReleaseBuild = $false, [Parameter(Mandatory=$False)] [array] $PackageInfoFiles ) # Validate-All-Packages.ps1 folds in the code that was originally in Validate-Package.ps1 # since Validate-Package.ps1 was only called from Validate-All-Packages.ps1. This replaces # script calls with function calls and also allows calling CheckAzLoginAndDevOpsExtensionInstall # and CheckDevOpsAccess once for all of the PackageInfo files being processed instead of once # per artifact in Validate-Package.ps1 and then again in Update-DevOps-Release-WorkItem.ps1 Set-StrictMode -Version 3 . (Join-Path $PSScriptRoot common.ps1) . (Join-Path $PSScriptRoot Helpers\ApiView-Helpers.ps1) . (Join-Path $PSScriptRoot Helpers\DevOps-WorkItem-Helpers.ps1) # Function to validate change log function ValidateChangeLog($changeLogPath, $versionString, $validationStatus) { try { $ChangeLogStatus = [PSCustomObject]@{ IsValid = $false Message = "" } $changeLogFullPath = Join-Path $RepoRoot $changeLogPath Write-Host "Path to change log: [$changeLogFullPath]" if (Test-Path $changeLogFullPath) { Confirm-ChangeLogEntry -ChangeLogLocation $changeLogFullPath -VersionString $versionString -ForRelease $true -ChangeLogStatus $ChangeLogStatus -SuppressErrors $true $validationStatus.Status = if ($ChangeLogStatus.IsValid) { "Success" } else { "Failed" } $validationStatus.Message = $ChangeLogStatus.Message } else { $validationStatus.Status = "Failed" $validationStatus.Message = "Change log is not found in [$changeLogPath]. Change log file must be present in package root directory." } } catch { Write-Host "Current directory: $(Get-Location)" $validationStatus.Status = "Failed" $validationStatus.Message = $_.Exception.Message } } # Function to verify API review status function VerifyAPIReview($packageName, $packageVersion, $language) { $APIReviewValidation = [PSCustomObject]@{ Name = "API Review Approval" Status = "Pending" Message = "" } $PackageNameValidation = [PSCustomObject]@{ Name = "Package Name Approval" Status = "Pending" Message = "" } try { $apiStatus = [PSCustomObject]@{ IsApproved = $false Details = "" } $packageNameStatus = [PSCustomObject]@{ IsApproved = $false Details = "" } Write-Host "Checking API review status for package $packageName with version $packageVersion. language [$language]." Check-ApiReviewStatus $packageName $packageVersion $language $APIViewUri $APIKey $apiStatus $packageNameStatus Write-Host "API review approval details: $($apiStatus.Details)" Write-Host "Package name approval details: $($packageNameStatus.Details)" #API review approval status $APIReviewValidation.Message = $apiStatus.Details $APIReviewValidation.Status = if ($apiStatus.IsApproved) { "Approved" } else { "Pending" } # Package name approval status $PackageNameValidation.Status = if ($packageNameStatus.IsApproved) { "Approved" } else { "Pending" } $PackageNameValidation.Message = $packageNameStatus.Details } catch { Write-Warning "Failed to get API review status. Error: $_" $PackageNameValidation.Status = "Failed" $PackageNameValidation.Message = $_.Exception.Message $APIReviewValidation.Status = "Failed" $APIReviewValidation.Message = $_.Exception.Message } return [PSCustomObject]@{ ApiviewApproval = $APIReviewValidation PackageNameApproval = $PackageNameValidation } } function IsVersionShipped($packageName, $packageVersion, $groupId = $null) { # This function will decide if a package version is already shipped or not Write-Host "Checking if a version is already shipped for package $packageName with version $packageVersion." $parsedNewVersion = [AzureEngSemanticVersion]::new($packageVersion) $versionMajorMinor = "" + $parsedNewVersion.Major + "." + $parsedNewVersion.Minor $workItem = FindPackageWorkItem -lang $LanguageDisplayName -packageName $packageName -groupId $groupId -version $versionMajorMinor -includeClosed $true -outputCommand $false if ($workItem) { # Check if the package version is already shipped $shippedVersionSet = ParseVersionSetFromMDField $workItem.fields["Custom.ShippedPackages"] if ($shippedVersionSet.ContainsKey($packageVersion)) { return $true } } else { Write-Host "No work item found for package [$packageName], group [$groupId]. Creating new work item for package." } return $false } function CreateUpdatePackageWorkItem($pkgInfo) { # This function will create or update package work item in Azure DevOps $versionString = $pkgInfo.Version $packageName = $pkgInfo.Name $plannedDate = $pkgInfo.ReleaseStatus $setReleaseState = $true if (!$plannedDate -or $plannedDate -eq "Unreleased") { $setReleaseState = $false $plannedDate = "unknown" } # Create or update package work item $result = Update-DevOpsReleaseWorkItem -language $LanguageDisplayName ` -packageName $packageName ` -groupId $pkgInfo.Group ` -version $versionString ` -plannedDate $plannedDate ` -packageRepoPath $pkgInfo.serviceDirectory ` -packageType $pkgInfo.SDKType ` -packageNewLibrary $pkgInfo.IsNewSDK ` -serviceName "unknown" ` -packageDisplayName "unknown" ` -inRelease $IsReleaseBuild ` -specProjectPath $pkgInfo.SpecProjectPath if (-not $result) { Write-Host "Update of the Devops Release WorkItem failed." } return [bool]$result } function ProcessPackage($packageInfo) { # Read package property file and identify all packages to process # $packageInfo.Name is the package name published to package managers, e.g. @azure/azure-template # $packageInfo.ArtifactName is the name can be used in path and file names, e.g. azure-template Write-Host "Processing artifact: $($packageInfo.ArtifactName)" Write-Host "Is Release Build: $IsReleaseBuild" $pkgName = $packageInfo.Name $changeLogPath = $packageInfo.ChangeLogPath $versionString = $packageInfo.Version Write-Host "Checking if we need to create or update work item for package $pkgName and groupId $packageInfo.Group with version $versionString." Write-Host "Package name before checking groupId: $pkgName" $isShipped = IsVersionShipped $pkgName $versionString $packageInfo.Group if ($isShipped) { Write-Host "Package work item already exists for version [$versionString] that is marked as shipped. Skipping the update of package work item." return } # Change log validation $changeLogStatus = [PSCustomObject]@{ Name = "Change Log Validation" Status = "Success" Message = "" } ValidateChangeLog $changeLogPath $versionString $changeLogStatus # API review and package name validation $fullPackageName = $pkgName # If there's a groupId that means this is Java and pkgName = GroupId+ArtifactName # but the VerifyAPIReview requires GroupId:ArtifactName # Technically we can use groupId+artifactName format in api view, # however it will need to migrate the existing data and Java parser also needs the change. $fullPackageName = Get-FullPackageName -PackageInfo $packageInfo -UseColonSeparator Write-Host "Checking API review status for package $fullPackageName" $apireviewDetails = VerifyAPIReview $fullPackageName $packageInfo.Version $Language # The following object will be used to update package work item, the name should be package name only without groupId $pkgValidationDetails= [PSCustomObject]@{ Name = $pkgName GroupId = $packageInfo.Group Version = $packageInfo.Version ChangeLogValidation = $changeLogStatus APIReviewValidation = $apireviewDetails.ApiviewApproval PackageNameValidation = $apireviewDetails.PackageNameApproval } $output = ConvertTo-Json $pkgValidationDetails Write-Host "Output: $($output)" # Create json token file in artifact path # Does the following validation file name also need to use full package name with groupId? $tokenFile = Join-Path $ArtifactPath "$($packageInfo.ArtifactName)-Validation.json" $output | Out-File -FilePath $tokenFile -Encoding utf8 # Create DevOps work item $updatedWi = CreateUpdatePackageWorkItem $packageInfo # Update validation status in package work item if ($updatedWi) { Write-Host "Updating validation status in package work item." $updatedWi = UpdateValidationStatus $pkgValidationDetails $BuildDefinition $PipelineUrl } # Fail the build if any validation is not successful for a release build Write-Host "Change log status:" $changeLogStatus.Status Write-Host "API Review status:" $apireviewDetails.ApiviewApproval.Status Write-Host "Package Name status:" $apireviewDetails.PackageNameApproval.Status if ($IsReleaseBuild) { if (!$updatedWi -or $changeLogStatus.Status -ne "Success" -or $apireviewDetails.ApiviewApproval.Status -ne "Approved" -or $apireviewDetails.PackageNameApproval.Status -ne "Approved") { Write-Error "At least one of the Validations above failed for package $pkgName with version $versionString." exit 1 } } } CheckAzLoginAndDevOpsExtensionInstall CheckDevOpsAccess Write-Host "Artifact path: $($ArtifactPath)" Write-Host "Artifact List: $($ArtifactList -join ', ')" Write-Host "Package Info Files: $($PackageInfoFiles -join ', ')" Write-Host "IsReleaseBuild: $IsReleaseBuild" # Check if package config file is present. This file has package version, SDK type etc info. if (-not $ConfigFileDir) { $ConfigFileDir = Join-Path -Path $ArtifactPath "PackageInfo" } Write-Host "Config file path: $($ConfigFileDir)" # Initialize working variable $ProcessedPackageInfoFiles = @() if ($ArtifactList -and $ArtifactList.Count -gt 0) { # Multiple artifacts mode (existing usage) Write-Host "Using ArtifactList parameter with $($ArtifactList.Count) artifacts" foreach ($artifact in $ArtifactList) { $pkgPropPath = Join-Path -Path $ConfigFileDir "$($artifact.name).json" if (Test-Path $pkgPropPath) { $ProcessedPackageInfoFiles += $pkgPropPath } else { Write-Warning "Package property file path $pkgPropPath is invalid." } } } elseif ($PackageInfoFiles -and $PackageInfoFiles.Count -gt 0) { # Direct PackageInfoFiles (new method) Write-Host "Using PackageInfoFiles parameter with $($PackageInfoFiles.Count) files" # Filter out empty strings or whitespace-only entries $ProcessedPackageInfoFiles = @($PackageInfoFiles | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) } # Validate that we have package info files to process if (-not $ProcessedPackageInfoFiles -or $ProcessedPackageInfoFiles.Count -eq 0) { Write-Error "No package info files found after processing parameters. Or PackageInfoFiles parameter contains only empty or whitespace entries, please check the artifact settings." exit 1 } Write-Host "Processed Package Info Files: $($ProcessedPackageInfoFiles -join ', ')" # Process all packages using the processed PackageInfoFiles array foreach ($packageInfoFile in $ProcessedPackageInfoFiles) { $packageInfo = Get-Content $packageInfoFile | ConvertFrom-Json ProcessPackage -packageInfo $packageInfo }