diff --git a/Cargo.lock b/Cargo.lock index fd1ef3877a..d9798029c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,6 +184,7 @@ name = "azure_canary_core" version = "0.1.0" dependencies = [ "azure_canary", + "azure_core", ] [[package]] diff --git a/eng/common/pipelines/templates/archetype-typespec-emitter.yml b/eng/common/pipelines/templates/archetype-typespec-emitter.yml index c359c3b1ae..443ac86a9f 100644 --- a/eng/common/pipelines/templates/archetype-typespec-emitter.yml +++ b/eng/common/pipelines/templates/archetype-typespec-emitter.yml @@ -137,17 +137,56 @@ extends: $sourceBranch = '$(Build.SourceBranch)' $buildReason = '$(Build.Reason)' $buildNumber = '$(Build.BuildNumber)' + $emitterPackagePath = '${{ parameters.EmitterPackagePath }}' + + # Create emitter identifier from package path for disambiguation + $emitterIdentifier = "" + if (-not [string]::IsNullOrWhiteSpace($emitterPackagePath)) { + # Resolve emitterPackagePath to absolute path (it's relative to repo root) + # EmitterPackagePath is a directory, so append package.json + $absoluteEmitterPackagePath = Join-Path '$(Build.SourcesDirectory)' $emitterPackagePath + $packageJsonPath = Join-Path $absoluteEmitterPackagePath 'package.json' + + # Read the package name from package.json + if (Test-Path $packageJsonPath) { + try { + $packageJson = Get-Content $packageJsonPath -Raw | ConvertFrom-Json + if ($packageJson.name) { + $emitterIdentifier = $packageJson.name + } + } catch { + Write-Host "Warning: Could not read package name from $packageJsonPath" + } + } + + # If we still don't have an identifier, fall back to filename + if ([string]::IsNullOrWhiteSpace($emitterIdentifier)) { + Write-Host "Warning: Could not read emitter name from package.json, falling back to package path" + $emitterIdentifier = [System.IO.Path]::GetFileNameWithoutExtension($emitterPackagePath) + } + + # Clean up the identifier: remove @ prefix, replace invalid chars + $emitterIdentifier = $emitterIdentifier -replace '^@', '' + $emitterIdentifier = $emitterIdentifier -replace '[^a-zA-Z0-9\-_/]', '-' + $emitterIdentifier = $emitterIdentifier.Trim('-').ToLower() + + if (-not [string]::IsNullOrWhiteSpace($emitterIdentifier)) { + $emitterIdentifier = "-$emitterIdentifier" + } + } if ($buildReason -eq 'Schedule') { - $branchName = 'validate-typespec-scheduled' + $branchName = "validate-typespec-scheduled$emitterIdentifier" } elseif ($sourceBranch -match "^refs/pull/(\d+)/(head|merge)$") { - $branchName = "validate-typespec-pr-$($Matches[1])" + $branchName = "validate-typespec-pr-$($Matches[1])$emitterIdentifier" } else { - $branchName = "validate-typespec-$buildNumber" + $branchName = "validate-typespec-$buildNumber$emitterIdentifier" } Write-Host "Setting variable 'branchName' to '$branchName'" Write-Host "##vso[task.setvariable variable=branchName;isOutput=true]$branchName" + Write-Host "Setting variable 'emitterIdentifier' to '$emitterIdentifier'" + Write-Host "##vso[task.setvariable variable=emitterIdentifier;isOutput=true]$emitterIdentifier" displayName: Set branch name name: set_branch_name @@ -259,27 +298,34 @@ extends: displayName: Download pipeline artifacts - pwsh: | - npm install -g @azure-tools/typespec-client-generator-cli@latest + npm ci displayName: Install tsp-client + workingDirectory: $(Build.SourcesDirectory)/eng/common/tsp-client - pwsh: | + # Resolve EmitterPackageJsonOutputPath to absolute path if it's relative + $emitterPath = '${{ parameters.EmitterPackageJsonOutputPath }}' + if (-not [System.IO.Path]::IsPathRooted($emitterPath)) { + $emitterPath = Join-Path '$(Build.SourcesDirectory)' $emitterPath + } + Write-Host "Overrides location: $(buildArtifactsPath)/packages/overrides.json" + Write-Host "Resolved emitter package path: $emitterPath" if (Test-Path -Path '$(buildArtifactsPath)/packages/overrides.json') { Write-Host "Using overrides.json to generate emitter-package.json" - tsp-client generate-config-files ` - --package-json '$(buildArtifactsPath)/lock-files/package.json' ` - --emitter-package-json-path '${{ parameters.EmitterPackageJsonOutputPath }}' ` - --overrides '$(buildArtifactsPath)/packages/overrides.json' + npm exec --no -- tsp-client generate-config-files ` + --package-json '$(buildArtifactsPath)/lock-files/package.json' ` + --emitter-package-json-path "$emitterPath" ` + --overrides '$(buildArtifactsPath)/packages/overrides.json' } else { Write-Host "No overrides.json found. Running tsp-client without overrides." - - tsp-client generate-config-files ` - --package-json '$(buildArtifactsPath)/lock-files/package.json' ` - --emitter-package-json-path '${{ parameters.EmitterPackageJsonOutputPath }}' + npm exec --no -- tsp-client generate-config-files ` + --package-json '$(buildArtifactsPath)/lock-files/package.json' ` + --emitter-package-json-path "$emitterPath" } displayName: Generate emitter-package.json and emitter-package-lock files - workingDirectory: $(Build.SourcesDirectory) + workingDirectory: $(Build.SourcesDirectory)/eng/common/tsp-client - ${{ parameters.InitializationSteps }} @@ -361,15 +407,18 @@ extends: displayName: Create PR dependsOn: - Generate + condition: succeededOrFailed() variables: generateJobResult: $[dependencies.Generate.result] emitterVersion: $[stageDependencies.Build.Build.outputs['initialize.emitterVersion']] + emitterIdentifier: $[stageDependencies.Build.Build.outputs['set_branch_name.emitterIdentifier']] steps: - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - pwsh: | $generateJobResult = '$(generateJobResult)' $emitterVersion = '$(emitterVersion)' + $emitterIdentifier = '$(emitterIdentifier)' $collectionUri = '$(System.CollectionUri)' $project = '$(System.TeamProject)' $definitionName = '$(Build.DefinitionName)' @@ -381,6 +430,12 @@ extends: $buildNumber = '$(Build.BuildNumber)' $preRelease = '${{ parameters.BuildPrereleaseVersion }}' -eq 'true' + # Use emitterIdentifier for PR title (remove leading dash if present) + $emitterName = "TypeSpec emitter" + if (-not [string]::IsNullOrWhiteSpace($emitterIdentifier)) { + $emitterName = $emitterIdentifier.TrimStart('-') + } + $prBody = "Generated by $definitionName build [$buildNumber]($collectionUri/$project/_build/results?buildId=$buildId)
" if ($sourceBranch -match "^refs/heads/(.+)$") { @@ -397,9 +452,9 @@ extends: $prTitle = "Scheduled code regeneration test" } else { if ($preRelease) { - $prTitle = "Update TypeSpec emitter version to prerelease $emitterVersion" + $prTitle = "Update $emitterName version to prerelease $emitterVersion" } else { - $prTitle = "Update TypeSpec emitter version to $emitterVersion" + $prTitle = "Update $emitterName version to $emitterVersion" } if ($generateJobResult -ne 'Succeeded') { @@ -424,7 +479,8 @@ extends: Write-Error "Build.Repository.Name not in the expected {Owner}/{Name} format" } - $openAsDraft = -not ($reason -eq 'IndividualCI' -and $sourceBranch -eq 'refs/heads/main') + # Open PR as draft if generation failed, or if it's not an IndividualCI build from main + $openAsDraft = ($generateJobResult -ne 'Succeeded') -or (-not ($reason -eq 'IndividualCI' -and $sourceBranch -eq 'refs/heads/main')) Write-Host "Setting OpenAsDraftBool = $openAsDraft" Write-Host "##vso[task.setvariable variable=OpenAsDraft]$openAsDraft" if ($openAsDraft) { diff --git a/eng/common/pipelines/templates/steps/create-apireview.yml b/eng/common/pipelines/templates/steps/create-apireview.yml index 124e027308..6942abd0ba 100644 --- a/eng/common/pipelines/templates/steps/create-apireview.yml +++ b/eng/common/pipelines/templates/steps/create-apireview.yml @@ -1,27 +1,47 @@ parameters: - ArtifactPath: $(Build.ArtifactStagingDirectory) - Artifacts: [] - ConfigFileDir: $(Build.ArtifactStagingDirectory)/PackageInfo - MarkPackageAsShipped: false - GenerateApiReviewForManualOnly: false - ArtifactName: 'packages' - PackageName: '' - SourceRootPath: $(Build.SourcesDirectory) + - name: ArtifactPath + type: string + default: $(Build.ArtifactStagingDirectory) + - name: Artifacts + type: object + default: [] + - name: ConfigFileDir + type: string + default: $(Build.ArtifactStagingDirectory)/PackageInfo + - name: MarkPackageAsShipped + type: boolean + default: false + - name: GenerateApiReviewForManualOnly + type: boolean + default: false + - name: ArtifactName + type: string + default: 'packages' + - name: PackageName + type: string + default: '' + - name: SourceRootPath + type: string + default: $(Build.SourcesDirectory) + - name: PackageInfoFiles + type: object + default: [] steps: - # ideally this should be done as initial step of a job in caller template - # We can remove this step later once it is added in caller - - template: /eng/common/pipelines/templates/steps/set-default-branch.yml - parameters: - WorkingDirectory: ${{ parameters.SourceRootPath }} - # Automatic API review is generated for a package when pipeline runs irrespective of how pipeline gets triggered. - # Below condition ensures that API review is generated only for manual pipeline runs when flag GenerateApiReviewForManualOnly is set to true. + # Below condition ensures that API review is generated only for manual pipeline runs when flag GenerateApiReviewForManualOnly is set to true. - ${{ if or(ne(parameters.GenerateApiReviewForManualOnly, true), eq(variables['Build.Reason'], 'Manual')) }}: + # ideally this should be done as initial step of a job in caller template + # We can remove this step later once it is added in caller + - template: /eng/common/pipelines/templates/steps/set-default-branch.yml + parameters: + WorkingDirectory: ${{ parameters.SourceRootPath }} + - task: Powershell@2 inputs: filePath: ${{ parameters.SourceRootPath }}/eng/common/scripts/Create-APIReview.ps1 arguments: > + -PackageInfoFiles ('${{ convertToJson(parameters.PackageInfoFiles) }}' | ConvertFrom-Json -NoEnumerate) -ArtifactList ('${{ convertToJson(parameters.Artifacts) }}' | ConvertFrom-Json | Select-Object Name) -ArtifactPath '${{parameters.ArtifactPath}}' -ArtifactName ${{ parameters.ArtifactName }} @@ -31,7 +51,7 @@ steps: -DefaultBranch '$(DefaultBranch)' -ConfigFileDir '${{parameters.ConfigFileDir}}' -BuildId '$(Build.BuildId)' - -RepoName '$(Build.Repository.Name)' + -RepoName '$(Build.Repository.Name)' -MarkPackageAsShipped $${{parameters.MarkPackageAsShipped}} pwsh: true displayName: Create API Review diff --git a/eng/common/pipelines/templates/steps/detect-api-changes.yml b/eng/common/pipelines/templates/steps/detect-api-changes.yml index 3144f0b2f3..fadeaacc67 100644 --- a/eng/common/pipelines/templates/steps/detect-api-changes.yml +++ b/eng/common/pipelines/templates/steps/detect-api-changes.yml @@ -10,7 +10,7 @@ steps: $apiChangeDetectRequestUrl = "https://apiview.dev/api/PullRequests/CreateAPIRevisionIfAPIHasChanges" echo "##vso[task.setvariable variable=ApiChangeDetectRequestUrl]$apiChangeDetectRequestUrl" displayName: "Set API change detect request URL" - condition: and(succeeded(), ${{ parameters.Condition}}, eq(variables['ApiChangeDetectRequestUrl'], '')) + condition: and(succeededOrFailed(), ${{ parameters.Condition}}, eq(variables['ApiChangeDetectRequestUrl'], '')) - task: Powershell@2 inputs: diff --git a/eng/common/pipelines/templates/steps/set-test-pipeline-version.yml b/eng/common/pipelines/templates/steps/set-test-pipeline-version.yml index 61d49cdb7d..cb4d8ff333 100644 --- a/eng/common/pipelines/templates/steps/set-test-pipeline-version.yml +++ b/eng/common/pipelines/templates/steps/set-test-pipeline-version.yml @@ -1,12 +1,25 @@ parameters: - PackageName: '' - PackageNames: '' - ServiceDirectory: '' - TagSeparator: '_' - TestPipeline: false +- name: PackageName + type: string + default: '' +- name: PackageNames + type: string + default: '' +- name: ServiceDirectory + type: string + default: '' +- name: TagSeparator + type: string + default: '_' +- name: TestPipeline + type: boolean + default: false +- name: ArtifactsJson + type: string + default: '' steps: -- ${{ if eq(parameters.TestPipeline, 'true') }}: +- ${{ if eq(parameters.TestPipeline, true) }}: - task: PowerShell@2 displayName: Prep template pipeline for release condition: and(succeeded(), ne(variables['Skip.SetTestPipelineVersion'], 'true')) @@ -18,4 +31,5 @@ steps: -PackageNames '${{ coalesce(parameters.PackageName, parameters.PackageNames) }}' -ServiceDirectory '${{ parameters.ServiceDirectory }}' -TagSeparator '${{ parameters.TagSeparator }}' + -ArtifactsJson '${{ parameters.ArtifactsJson }}' pwsh: true diff --git a/eng/common/scripts/Create-APIReview.ps1 b/eng/common/scripts/Create-APIReview.ps1 index 5438df0671..92e614f4a2 100644 --- a/eng/common/scripts/Create-APIReview.ps1 +++ b/eng/common/scripts/Create-APIReview.ps1 @@ -1,11 +1,11 @@ [CmdletBinding()] Param ( - [Parameter(Mandatory=$True)] + [Parameter(Mandatory=$False)] [array] $ArtifactList, [Parameter(Mandatory=$True)] - [string] $ArtifactPath, + [string] $ArtifactPath, [Parameter(Mandatory=$True)] - [string] $APIKey, + [string] $APIKey, [string] $SourceBranch, [string] $DefaultBranch, [string] $RepoName, @@ -14,7 +14,9 @@ Param ( [string] $ConfigFileDir = "", [string] $APIViewUri = "https://apiview.dev/AutoReview", [string] $ArtifactName = "packages", - [bool] $MarkPackageAsShipped = $false + [bool] $MarkPackageAsShipped = $false, + [Parameter(Mandatory=$False)] + [array] $PackageInfoFiles ) Set-StrictMode -Version 3 @@ -51,7 +53,7 @@ function Upload-SourceArtifact($filePath, $apiLabel, $releaseStatus, $packageVer $versionContent.Headers.ContentDisposition = $versionParam $multipartContent.Add($versionContent) Write-Host "Request param, packageVersion: $packageVersion" - + $releaseTagParam = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data") $releaseTagParam.Name = "setReleaseTag" $releaseTagParamContent = [System.Net.Http.StringContent]::new($MarkPackageAsShipped) @@ -96,7 +98,7 @@ function Upload-ReviewTokenFile($packageName, $apiLabel, $releaseStatus, $review $fileName = Split-Path -Leaf $filePath Write-Host "OriginalFile name: $fileName" - $params = "buildId=${BuildId}&artifactName=${ArtifactName}&originalFilePath=${fileName}&reviewFilePath=${reviewFileName}" + $params = "buildId=${BuildId}&artifactName=${ArtifactName}&originalFilePath=${fileName}&reviewFilePath=${reviewFileName}" $params += "&label=${apiLabel}&repoName=${RepoName}&packageName=${packageName}&project=internal&packageVersion=${packageVersion}" if($MarkPackageAsShipped) { $params += "&setReleaseTag=true" @@ -141,17 +143,16 @@ function Get-APITokenFileName($packageName) } } -function Submit-APIReview($packageInfo, $packagePath, $packageArtifactName) +function Submit-APIReview($packageInfo, $packagePath) { - $packageName = $packageInfo.Name $apiLabel = "Source Branch:${SourceBranch}" # Get generated review token file if present # APIView processes request using different API if token file is already generated - $reviewTokenFileName = Get-APITokenFileName $packageArtifactName + $reviewTokenFileName = Get-APITokenFileName $packageInfo.ArtifactName if ($reviewTokenFileName) { Write-Host "Uploading review token file $reviewTokenFileName to APIView." - return Upload-ReviewTokenFile $packageArtifactName $apiLabel $packageInfo.ReleaseStatus $reviewTokenFileName $packageInfo.Version $packagePath + return Upload-ReviewTokenFile $packageInfo.ArtifactName $apiLabel $packageInfo.ReleaseStatus $reviewTokenFileName $packageInfo.Version $packagePath } else { Write-Host "Uploading $packagePath to APIView." @@ -172,12 +173,32 @@ function IsApiviewStatusCheckRequired($packageInfo) return $false } -function ProcessPackage($packageName) +function ProcessPackage($packageInfo) { $packages = @{} if ($FindArtifactForApiReviewFn -and (Test-Path "Function:$FindArtifactForApiReviewFn")) { - $packages = &$FindArtifactForApiReviewFn $ArtifactPath $packageName + $pkgArtifactName = $packageInfo.ArtifactName ?? $packageInfo.Name + + # Check if the function supports the packageInfo parameter + $functionInfo = Get-Command $FindArtifactForApiReviewFn -ErrorAction SilentlyContinue + $supportsPackageInfoParam = $false + + if ($functionInfo -and $functionInfo.Parameters) { + # Check if function specifically supports packageInfo parameter + $parameterNames = $functionInfo.Parameters.Keys + $supportsPackageInfoParam = $parameterNames -contains 'packageInfo' + } + + # Call function with appropriate parameters + if ($supportsPackageInfoParam) { + LogInfo "Calling $FindArtifactForApiReviewFn with packageInfo parameter" + $packages = &$FindArtifactForApiReviewFn $ArtifactPath $packageInfo + } + else { + LogInfo "Calling $FindArtifactForApiReviewFn with legacy parameters" + $packages = &$FindArtifactForApiReviewFn $ArtifactPath $pkgArtifactName + } } else { @@ -192,31 +213,23 @@ function ProcessPackage($packageName) foreach($pkgPath in $packages.Values) { $pkg = Split-Path -Leaf $pkgPath - $pkgPropPath = Join-Path -Path $ConfigFileDir "$packageName.json" - if (-Not (Test-Path $pkgPropPath)) - { - Write-Host " Package property file path $($pkgPropPath) is invalid." - continue - } - # Get package info from json file created before updating version to daily dev - $pkgInfo = Get-Content $pkgPropPath | ConvertFrom-Json - $version = [AzureEngSemanticVersion]::ParseVersionString($pkgInfo.Version) - if ($version -eq $null) + $version = [AzureEngSemanticVersion]::ParseVersionString($packageInfo.Version) + if ($null -eq $version) { - Write-Host "Version info is not available for package $packageName, because version '$(pkgInfo.Version)' is invalid. Please check if the version follows Azure SDK package versioning guidelines." + Write-Host "Version info is not available for package $($packageInfo.ArtifactName), because version '$($packageInfo.Version)' is invalid. Please check if the version follows Azure SDK package versioning guidelines." return 1 } - + Write-Host "Version: $($version)" - Write-Host "SDK Type: $($pkgInfo.SdkType)" - Write-Host "Release Status: $($pkgInfo.ReleaseStatus)" + Write-Host "SDK Type: $($packageInfo.SdkType)" + Write-Host "Release Status: $($packageInfo.ReleaseStatus)" # Run create review step only if build is triggered from main branch or if version is GA. # This is to avoid invalidating review status by a build triggered from feature branch if ( ($SourceBranch -eq $DefaultBranch) -or (-not $version.IsPrerelease) -or $MarkPackageAsShipped) { Write-Host "Submitting API Review request for package $($pkg), File path: $($pkgPath)" - $respCode = Submit-APIReview $pkgInfo $pkgPath $packageName + $respCode = Submit-APIReview $packageInfo $pkgPath Write-Host "HTTP Response code: $($respCode)" # no need to check API review status when marking a package as shipped @@ -224,10 +237,10 @@ function ProcessPackage($packageName) { if ($respCode -eq '500') { - Write-Host "Failed to mark package ${packageName} as released. Please reach out to Azure SDK engineering systems on teams channel." + Write-Host "Failed to mark package $($packageInfo.ArtifactName) as released. Please reach out to Azure SDK engineering systems on teams channel." return 1 } - Write-Host "Package ${packageName} is marked as released." + Write-Host "Package $($packageInfo.ArtifactName) is marked as released." return 0 } @@ -239,41 +252,41 @@ function ProcessPackage($packageName) IsApproved = $false Details = "" } - Process-ReviewStatusCode $respCode $packageName $apiStatus $pkgNameStatus + Process-ReviewStatusCode $respCode $packageInfo.ArtifactName $apiStatus $pkgNameStatus if ($apiStatus.IsApproved) { Write-Host "API status: $($apiStatus.Details)" } - elseif (!$pkgInfo.ReleaseStatus -or $pkgInfo.ReleaseStatus -eq "Unreleased") { + elseif (!$packageInfo.ReleaseStatus -or $packageInfo.ReleaseStatus -eq "Unreleased") { Write-Host "Release date is not set for current version in change log file for package. Ignoring API review approval status since package is not yet ready for release." } elseif ($version.IsPrerelease) { # Check if package name is approved. Preview version cannot be released without package name approval - if (!$pkgNameStatus.IsApproved) + if (!$pkgNameStatus.IsApproved) { - if (IsApiviewStatusCheckRequired $pkgInfo) + if (IsApiviewStatusCheckRequired $packageInfo) { Write-Error $($pkgNameStatus.Details) return 1 } else{ - Write-Host "Package name is not approved for package $($packageName), however it is not required for this package type so it can still be released without API review approval." - } + Write-Host "Package name is not approved for package $($packageInfo.ArtifactName), however it is not required for this package type so it can still be released without API review approval." + } } # Ignore API review status for prerelease version Write-Host "Package version is not GA. Ignoring API view approval status" - } + } else { # Return error code if status code is 201 for new data plane package # Temporarily enable API review for spring SDK types. Ideally this should be done be using 'IsReviewRequired' method in language side # to override default check of SDK type client - if (IsApiviewStatusCheckRequired $pkgInfo) + if (IsApiviewStatusCheckRequired $packageInfo) { if (!$apiStatus.IsApproved) { - Write-Host "Package version $($version) is GA and automatic API Review is not yet approved for package $($packageName)." + Write-Host "Package version $($version) is GA and automatic API Review is not yet approved for package $($packageInfo.ArtifactName)." Write-Host "Build and release is not allowed for GA package without API review approval." Write-Host "You will need to queue another build to proceed further after API review is approved" Write-Host "You can check http://aka.ms/azsdk/engsys/apireview/faq for more details on API Approval." @@ -281,7 +294,7 @@ function ProcessPackage($packageName) return 1 } else { - Write-Host "API review is not approved for package $($packageName), however it is not required for this package type so it can still be released without API review approval." + Write-Host "API review is not approved for package $($packageInfo.ArtifactName), however it is not required for this package type so it can still be released without API review approval." } } } @@ -296,42 +309,84 @@ function ProcessPackage($packageName) return 0 } -$responses = @{} -# Check if package config file is present. This file has package version, SDK type etc info. -if (-not $ConfigFileDir) -{ +Write-Host "Artifact path: $($ArtifactPath)" +Write-Host "Source branch: $($SourceBranch)" +Write-Host "Package Info Files: $($PackageInfoFiles)" +Write-Host "Artifact List: $($ArtifactList)" +Write-Host "Package Name: $($PackageName)" + +# Parameter priority and backward compatibility logic +# Priority order: PackageName > Artifacts > PackageInfoFiles (for backward compatibility) + +if (-not $ConfigFileDir) { $ConfigFileDir = Join-Path -Path $ArtifactPath "PackageInfo" } -Write-Host "Artifact path: $($ArtifactPath)" -Write-Host "Source branch: $($SourceBranch)" Write-Host "Config File directory: $($ConfigFileDir)" -# if package name param is not empty then process only that package -if ($PackageName) -{ - Write-Host "Processing $($PackageName)" - $result = ProcessPackage -packageName $PackageName - $responses[$PackageName] = $result +# Initialize working variable +$ProcessedPackageInfoFiles = @() + +if ($PackageName -and $PackageName -ne "") { + # Highest Priority: Single package mode (existing usage) + Write-Host "Using PackageName parameter: $PackageName" + $pkgPropPath = Join-Path -Path $ConfigFileDir "$PackageName.json" + if (Test-Path $pkgPropPath) { + $ProcessedPackageInfoFiles = @($pkgPropPath) + } + else { + Write-Error "Package property file path $pkgPropPath is invalid." + exit 1 + } } -else -{ - # process all packages in the artifact - foreach ($artifact in $ArtifactList) - { - Write-Host "Processing $($artifact.name)" - $result = ProcessPackage -packageName $artifact.name - $responses[$artifact.name] = $result +elseif ($ArtifactList -and $ArtifactList.Count -gt 0) { + # Second Priority: 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) { + # Lowest Priority: Direct PackageInfoFiles (new method) + Write-Host "Using PackageInfoFiles parameter with $($PackageInfoFiles.Count) files" + $ProcessedPackageInfoFiles = $PackageInfoFiles # Use as-is +} +else { + Write-Error "No package information provided. Please provide either 'PackageName', 'ArtifactList', or 'PackageInfoFiles' parameters." + exit 1 +} + +# 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." + exit 1 +} + +$responses = @{} +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 + Write-Host "Processing $($packageInfo.ArtifactName)" + $result = ProcessPackage -packageInfo $packageInfo + $responses[$packageInfo.ArtifactName] = $result +} $exitCode = 0 foreach($pkg in $responses.keys) -{ +{ if ($responses[$pkg] -eq 1) { Write-Host "API changes are not approved for $($pkg)" $exitCode = 1 } } -exit $exitCode \ No newline at end of file +exit $exitCode diff --git a/eng/common/scripts/Detect-Api-Changes.ps1 b/eng/common/scripts/Detect-Api-Changes.ps1 index 25f3101a8b..8c3807e318 100644 --- a/eng/common/scripts/Detect-Api-Changes.ps1 +++ b/eng/common/scripts/Detect-Api-Changes.ps1 @@ -80,22 +80,13 @@ function Submit-Request($filePath, $packageName) return $StatusCode } -function Should-Process-Package($pkgPath, $packageName) +function Should-Process-Package($packageInfo) { - $pkg = Split-Path -Leaf $pkgPath - $pkgPropPath = Join-Path -Path $configFileDir "$packageName.json" - if (!(Test-Path $pkgPropPath)) - { - LogWarning "Package property file path $($pkgPropPath) is invalid." - return $False - } - # Get package info from json file created before updating version to daily dev - $pkgInfo = Get-Content $pkgPropPath | ConvertFrom-Json - $packagePath = $pkgInfo.DirectoryPath + $packagePath = $packageInfo.DirectoryPath $modifiedFiles = @(Get-ChangedFiles -DiffPath "$packagePath/*" -DiffFilterType '') $filteredFileCount = $modifiedFiles.Count LogInfo "Number of modified files for package: $filteredFileCount" - return ($filteredFileCount -gt 0 -and $pkgInfo.IsNewSdk) + return ($filteredFileCount -gt 0 -and $packageInfo.IsNewSdk) } function Log-Input-Params() @@ -126,24 +117,42 @@ $responses = @{} LogInfo "Processing PackageInfo at $configFileDir" -$packageProperties = Get-ChildItem -Recurse -Force "$configFileDir" ` - | Where-Object { +$packageInfoFiles = Get-ChildItem -Recurse -Force "$configFileDir" ` + | Where-Object { $_.Extension -eq '.json' -and ($_.FullName.Substring($configFileDir.Length + 1) -notmatch '^_.*?[\\\/]') } -foreach ($packagePropFile in $packageProperties) +foreach ($packageInfoFile in $packageInfoFiles) { - $packageMetadata = Get-Content $packagePropFile | ConvertFrom-Json - $pkgArtifactName = $packageMetadata.ArtifactName ?? $packageMetadata.Name + $packageInfo = Get-Content $packageInfoFile | ConvertFrom-Json + $pkgArtifactName = $packageInfo.ArtifactName ?? $packageInfo.Name LogInfo "Processing $($pkgArtifactName)" - $packages = &$FindArtifactForApiReviewFn $ArtifactPath $pkgArtifactName + # Check if the function supports the packageInfo parameter + $functionInfo = Get-Command $FindArtifactForApiReviewFn -ErrorAction SilentlyContinue + $supportsPackageInfoParam = $false + + if ($functionInfo -and $functionInfo.Parameters) { + # Check if function specifically supports packageInfo parameter + $parameterNames = $functionInfo.Parameters.Keys + $supportsPackageInfoParam = $parameterNames -contains 'packageInfo' + } + + # Call function with appropriate parameters + if ($supportsPackageInfoParam) { + LogInfo "Calling $FindArtifactForApiReviewFn with packageInfo parameter" + $packages = &$FindArtifactForApiReviewFn $ArtifactPath $packageInfo + } + else { + LogInfo "Calling $FindArtifactForApiReviewFn with legacy parameters" + $packages = &$FindArtifactForApiReviewFn $ArtifactPath $pkgArtifactName + } if ($packages) { $pkgPath = $packages.Values[0] - $isRequired = Should-Process-Package -pkgPath $pkgPath -packageName $pkgArtifactName + $isRequired = Should-Process-Package $packageInfo LogInfo "Is API change detect required for $($pkgArtifactName):$($isRequired)" if ($isRequired -eq $True) { @@ -156,7 +165,7 @@ foreach ($packagePropFile in $packageProperties) } else { - LogInfo "Pull request does not have any change for $($pkgArtifactName)). Skipping API change detect." + LogInfo "Pull request does not have any change for $($pkgArtifactName). Skipping API change detect." } } else diff --git a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 index 79b9bb96da..e87b2c8a91 100644 --- a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 +++ b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 @@ -1114,7 +1114,7 @@ function Get-ReleasePlan-Link($releasePlanWorkItemId) return $workItem["fields"] } -function Get-ReleasePlansForCPEXAttestation($releasePlanWorkItemId = $null, $targetServiceTreeId = $null) +function Get-ReleasePlansForCPEXAttestation() { $fields = @() $fields += "Custom.ProductServiceTreeID" @@ -1127,32 +1127,23 @@ function Get-ReleasePlansForCPEXAttestation($releasePlanWorkItemId = $null, $tar $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " $query = "SELECT ${fieldList} FROM WorkItems WHERE [System.WorkItemType] = 'Release Plan'" - - if ($releasePlanWorkItemId){ - $query += " AND [System.Id] = '$releasePlanWorkItemId'" - } else { - $query += " AND [System.State] = 'Finished'" - $query += " AND [Custom.AttestationStatus] IN ('', 'Pending')" - $query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'" - $query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'" - $query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'" - $query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'" - $query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'" - $query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'" - $query += " AND [Custom.ProductServiceTreeID] <> ''" - $query += " AND [Custom.ProductLifecycle] <> ''" - $query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')" - } - - if ($targetServiceTreeId){ - $query += " AND [Custom.ProductServiceTreeID] = '${targetServiceTreeId}'" - } + $query += " AND [System.State] = 'Finished'" + $query += " AND [Custom.AttestationStatus] IN ('', 'Pending')" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'" + $query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'" + $query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'" + $query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'" + $query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'" + $query += " AND [Custom.ProductServiceTreeID] <> ''" + $query += " AND [Custom.ProductLifecycle] <> ''" + $query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')" $workItems = Invoke-Query $fields $query return $workItems } -function Get-TriagesForCPEXAttestation($triageWorkItemId = $null, $targetServiceTreeId = $null) +function Get-TriagesForCPEXAttestation() { $fields = @() $fields += "Custom.ProductServiceTreeID" @@ -1167,25 +1158,16 @@ function Get-TriagesForCPEXAttestation($triageWorkItemId = $null, $targetService $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " $query = "SELECT ${fieldList} FROM WorkItems WHERE [System.WorkItemType] = 'Triage'" - - if ($triageWorkItemId){ - $query += " AND [System.Id] = '$triageWorkItemId'" - } else { - $query += " AND ([Custom.DataplaneAttestationStatus] IN ('', 'Pending') OR [Custom.ManagementPlaneAttestationStatus] IN ('', 'Pending'))" - $query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'" - $query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'" - $query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'" - $query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'" - $query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'" - $query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'" - $query += " AND [Custom.ProductServiceTreeID] <> ''" - $query += " AND [Custom.ProductLifecycle] <> ''" - $query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')" - } - - if ($targetServiceTreeId){ - $query += " AND [Custom.ProductServiceTreeID] = '$targetServiceTreeId'" - } + $query += " AND ([Custom.DataplaneAttestationStatus] IN ('', 'Pending') OR [Custom.ManagementPlaneAttestationStatus] IN ('', 'Pending'))" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner App Test'" + $query += " AND [System.Tags] NOT CONTAINS 'Release Planner Test App'" + $query += " AND [System.Tags] NOT CONTAINS 'non-APEX tracking'" + $query += " AND [System.Tags] NOT CONTAINS 'out of scope APEX'" + $query += " AND [System.Tags] NOT CONTAINS 'APEX out of scope'" + $query += " AND [System.Tags] NOT CONTAINS 'validate APEX out of scope'" + $query += " AND [Custom.ProductServiceTreeID] <> ''" + $query += " AND [Custom.ProductLifecycle] <> ''" + $query += " AND [Custom.ProductType] IN ('Feature', 'Offering', 'Sku')" $workItems = Invoke-Query $fields $query return $workItems diff --git a/eng/common/scripts/SetTestPipelineVersion.ps1 b/eng/common/scripts/SetTestPipelineVersion.ps1 index 2b2ee70ef9..dea5496874 100644 --- a/eng/common/scripts/SetTestPipelineVersion.ps1 +++ b/eng/common/scripts/SetTestPipelineVersion.ps1 @@ -3,12 +3,14 @@ param ( [Parameter(mandatory = $true)] [string]$BuildID, - [Parameter(mandatory = $true)] - [string]$PackageNames, + [Parameter(mandatory = $false)] + [string]$PackageNames = "", [Parameter(mandatory = $true)] [string]$ServiceDirectory, [Parameter(mandatory = $false)] - [string]$TagSeparator = "_" + [string]$TagSeparator = "_", + [Parameter(mandatory = $false)] + [string]$ArtifactsJson = "" ) . (Join-Path $PSScriptRoot common.ps1) @@ -16,43 +18,117 @@ param ( Write-Host "PackageNames: $PackageNames" Write-Host "ServiceDirectory: $ServiceDirectory" Write-Host "BuildID: $BuildID" +Write-Host "ArtifactsJson: $ArtifactsJson" $packageNamesArray = @() +$artifacts = $null -if ([String]::IsNullOrWhiteSpace($PackageNames)) { - LogError "PackageNames cannot be empty." - exit 1 +# If ArtifactsJson is provided, extract package names from it +if (![String]::IsNullOrWhiteSpace($ArtifactsJson)) { + Write-Host "Using ArtifactsJson to determine package names" + try { + $artifacts = $ArtifactsJson | ConvertFrom-Json + $packageNamesArray = $artifacts | ForEach-Object { $_.name } + Write-Host "Extracted package names from ArtifactsJson: $($packageNamesArray -join ', ')" + } + catch { + LogError "Failed to parse ArtifactsJson: $($_.Exception.Message)" + exit 1 + } } -else { +elseif (![String]::IsNullOrWhiteSpace($PackageNames)) { $packageNamesArray = $PackageNames.Split(',') } +else { + LogError "Either PackageNames or ArtifactsJson must be provided." + exit 1 +} -foreach ($packageName in $packageNamesArray) { - Write-Host "Processing $packageName" - $newVersion = [AzureEngSemanticVersion]::new("1.0.0") - $prefix = "$packageName$TagSeparator" - Write-Host "Get Latest Tag : git tag -l $prefix*" - $latestTags = git tag -l "$prefix*" +if ($artifacts) { + # When using ArtifactsJson, process each artifact with its name and groupId (if applicable) + try { + foreach ($artifact in $artifacts) { + $packageName = $artifact.name + $newVersion = [AzureEngSemanticVersion]::new("1.0.0") + $prefix = "$packageName$TagSeparator" - $semVars = @() + if ($Language -eq "java") { + $groupId = $artifact.groupId + Write-Host "Processing $packageName with groupId $groupId" + if ([String]::IsNullOrWhiteSpace($groupId)) { + LogError "GroupId is missing for package $packageName." + exit 1 + } + # Use groupId+artifactName format for tag prefix (e.g., "com.azure.v2+azure-sdk-template_") + $prefix = "$groupId+$packageName$TagSeparator" + } - if ($latestTags -and ($latestTags.Length -gt 0)) { - foreach ($tag in $latestTags) { - $semVars += $tag.Substring($prefix.Length) - } + Write-Host "Get Latest Tag : git tag -l $prefix*" + $latestTags = git tag -l "$prefix*" + + $semVars = @() - $semVarsSorted = [AzureEngSemanticVersion]::SortVersionStrings($semVars) - Write-Host "Last Published Version $($semVarsSorted[0])" - $newVersion = [AzureEngSemanticVersion]::new($semVarsSorted[0]) + if ($latestTags -and ($latestTags.Length -gt 0)) { + foreach ($tag in $latestTags) { + $semVars += $tag.Substring($prefix.Length) + } + + $semVarsSorted = [AzureEngSemanticVersion]::SortVersionStrings($semVars) + Write-Host "Last Published Version $($semVarsSorted[0])" + $newVersion = [AzureEngSemanticVersion]::new($semVarsSorted[0]) + } + + $newVersion.PrereleaseLabel = $newVersion.DefaultPrereleaseLabel + $newVersion.PrereleaseNumber = $BuildID + $newVersion.IsPrerelease = $True + + Write-Host "Version to publish [ $($newVersion.ToString()) ]" + + if ($Language -ne "java") { + SetPackageVersion -PackageName $packageName ` + -Version $newVersion.ToString() ` + -ServiceDirectory $ServiceDirectory + } else { + SetPackageVersion -PackageName $packageName ` + -Version $newVersion.ToString() ` + -ServiceDirectory $ServiceDirectory ` + -GroupId $groupId + } + } + } + catch { + LogError "Failed to process ArtifactsJson: $ArtifactsJson, exception: $($_.Exception.Message)" + exit 1 } +} else { + # Fallback to original logic when using PackageNames string + foreach ($packageName in $packageNamesArray) { + Write-Host "Processing $packageName" + $newVersion = [AzureEngSemanticVersion]::new("1.0.0") + $prefix = "$packageName$TagSeparator" + Write-Host "Get Latest Tag : git tag -l $prefix*" + $latestTags = git tag -l "$prefix*" - $newVersion.PrereleaseLabel = $newVersion.DefaultPrereleaseLabel - $newVersion.PrereleaseNumber = $BuildID - $newVersion.IsPrerelease = $True + $semVars = @() - Write-Host "Version to publish [ $($newVersion.ToString()) ]" + if ($latestTags -and ($latestTags.Length -gt 0)) { + foreach ($tag in $latestTags) { + $semVars += $tag.Substring($prefix.Length) + } - SetPackageVersion -PackageName $packageName ` - -Version $newVersion.ToString() ` - -ServiceDirectory $ServiceDirectory + $semVarsSorted = [AzureEngSemanticVersion]::SortVersionStrings($semVars) + Write-Host "Last Published Version $($semVarsSorted[0])" + $newVersion = [AzureEngSemanticVersion]::new($semVarsSorted[0]) + } + + $newVersion.PrereleaseLabel = $newVersion.DefaultPrereleaseLabel + $newVersion.PrereleaseNumber = $BuildID + $newVersion.IsPrerelease = $True + + Write-Host "Version to publish [ $($newVersion.ToString()) ]" + + SetPackageVersion -PackageName $packageName ` + -Version $newVersion.ToString() ` + -ServiceDirectory $ServiceDirectory + } } diff --git a/eng/common/scripts/job-matrix/Create-JobMatrix.ps1 b/eng/common/scripts/job-matrix/Create-JobMatrix.ps1 index d35b3c923a..2ca67e92a7 100644 --- a/eng/common/scripts/job-matrix/Create-JobMatrix.ps1 +++ b/eng/common/scripts/job-matrix/Create-JobMatrix.ps1 @@ -54,7 +54,9 @@ LogGroupEnd $serialized = SerializePipelineMatrix $matrix Write-Host "Generated matrix:" -Write-Host $serialized.pretty + +# Write-Output required to support other scripts that call this script directly +Write-Output $serialized.pretty if ($CI) { Write-Output "##vso[task.setVariable variable=matrix;isOutput=true]$($serialized.compressed)" diff --git a/eng/common/tsp-client/README.md b/eng/common/tsp-client/README.md index c0a6076a4b..6f88d536c9 100644 --- a/eng/common/tsp-client/README.md +++ b/eng/common/tsp-client/README.md @@ -25,30 +25,32 @@ npm ci ## Usage -After installation, you can run tsp-client by navigating to the directory and using npm exec: +After installation, you can run `tsp-client` using `npm exec --prefix {path_to_the_eng/common/tsp-client}`. +Note that you should *not* navigate into the `eng/common/tsp-client` folder, since several `tsp-client` commands require the current working directory to be the client library's root. ```bash -cd eng/common/tsp-client +# Set the tsp-client directory path relative to your current working directory +_TspClientDir=eng/common/tsp-client # Get help -npm exec --no -- tsp-client --help +npm exec --prefix ${_TspClientDir} --no -- tsp-client --help # Check version -npm exec --no -- tsp-client version +npm exec --prefix ${_TspClientDir} --no -- tsp-client version # Generate client code -npm exec --no -- tsp-client generate --output-dir ./generated +npm exec --prefix ${_TspClientDir} --no -- tsp-client generate --output-dir ./generated # Initialize a new project -npm exec --no -- tsp-client init --tsp-config ./tspconfig.yaml +npm exec --prefix ${_TspClientDir} --no -- tsp-client init --tsp-config ./tspconfig.yaml ``` ## CI/CD Best Practices ```bash -cd eng/common/tsp-client -npm ci -npm exec --no -- tsp-client init --update-if-exists --tsp-config https://github.com/Azure/azure-rest-api-specs/blob/dee71463cbde1d416c47cf544e34f7966a94ddcb/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml +_TspClientDir=eng/common/tsp-client +npm ci --prefix ${_TspClientDir} +npm exec --prefix ${_TspClientDir} --no -- tsp-client init --update-if-exists --tsp-config https://github.com/Azure/azure-rest-api-specs/blob/dee71463cbde1d416c47cf544e34f7966a94ddcb/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml ``` ## Package Management diff --git a/eng/common/tsp-client/package-lock.json b/eng/common/tsp-client/package-lock.json index b998c1b4fc..0859bbbfb1 100644 --- a/eng/common/tsp-client/package-lock.json +++ b/eng/common/tsp-client/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "@azure-tools/typespec-client-generator-cli": "0.28.3" + "@azure-tools/typespec-client-generator-cli": "0.29.0" } }, "node_modules/@autorest/codemodel": { @@ -189,9 +189,9 @@ } }, "node_modules/@azure-tools/typespec-client-generator-cli": { - "version": "0.28.3", - "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-cli/-/typespec-client-generator-cli-0.28.3.tgz", - "integrity": "sha512-vWIuMHAlSC3mi+5YJregrxNSU3tZMkZZXTwCV53TX3f/eLYOCT8fTCmNHMOIseIX0OPPb4M1zoLFzZTr96Z7mQ==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-cli/-/typespec-client-generator-cli-0.29.0.tgz", + "integrity": "sha512-dQ8aHoouZ1SaDzan2xv2sa9Kzf/gXG3LFnas9io+3x6HzVCljlqiN/hPd8c35pn9mqVhlVoktg3CGlMKDwQN/Q==", "license": "MIT", "dependencies": { "@autorest/core": "^3.10.2", diff --git a/eng/common/tsp-client/package.json b/eng/common/tsp-client/package.json index aa5b6fff24..8aa229e7cd 100644 --- a/eng/common/tsp-client/package.json +++ b/eng/common/tsp-client/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "@azure-tools/typespec-client-generator-cli": "0.28.3" + "@azure-tools/typespec-client-generator-cli": "0.29.0" } } diff --git a/eng/pipelines/templates/jobs/pack.yml b/eng/pipelines/templates/jobs/pack.yml index 1d0f51a814..cbd258b83a 100644 --- a/eng/pipelines/templates/jobs/pack.yml +++ b/eng/pipelines/templates/jobs/pack.yml @@ -55,15 +55,52 @@ jobs: ServiceDirectory: ${{ parameters.ServiceDirectory }} PackageInfoDirectory: $(Build.ArtifactStagingDirectory)/PackageInfo - - task: Powershell@2 - displayName: "Pack Crates" - condition: and(succeeded(), ne(variables['NoPackagesChanged'],'true')) - inputs: - pwsh: true - filePath: $(Build.SourcesDirectory)/eng/scripts/Pack-Crates.ps1 - arguments: > - -OutputPath '$(Build.ArtifactStagingDirectory)' - -PackageInfoDirectory '$(Build.ArtifactStagingDirectory)/PackageInfo' + - ${{ if eq('auto', parameters.ServiceDirectory) }}: + - task: Powershell@2 + displayName: Pack Crates + condition: and(succeeded(), ne(variables['NoPackagesChanged'],'true')) + inputs: + pwsh: true + filePath: $(Build.SourcesDirectory)/eng/scripts/Pack-Crates.ps1 + arguments: > + -OutputPath '$(Build.ArtifactStagingDirectory)' + -PackageInfoDirectory '$(Build.ArtifactStagingDirectory)/PackageInfo' + + - ${{ else }}: + - pwsh: | + $artifacts = '${{ convertToJson(parameters.Artifacts) }}' | ConvertFrom-Json + $isReleaseBuild = $true + $artifactsToBuild = $artifacts | Where-Object { $_.releaseInBatch -eq 'True' } + + if (!$artifactsToBuild) { + Write-Host "No packages to release. Building all packages in the service directory with no dependency validation." + $artifactsToBuild = $artifacts + $isReleaseBuild = $false + } + + $packageNames = $artifactsToBuild.name + + Write-Host "##vso[task.setvariable variable=PackageNames]$($packageNames -join ',')" + if ($isReleaseBuild) { + Write-Host "##vso[task.setvariable variable=AdditionalPackageParams]-Release" + } else { + Write-Host "##vso[task.setvariable variable=AdditionalPackageParams]" + } + displayName: Configure crate packing + condition: and(succeeded(), ne(variables['NoPackagesChanged'],'true')) + + - task: Powershell@2 + displayName: Pack Crates + condition: and(succeeded(), ne(variables['NoPackagesChanged'],'true')) + inputs: + pwsh: true + filePath: $(Build.SourcesDirectory)/eng/scripts/Pack-Crates.ps1 + arguments: > + -OutputPath '$(Build.ArtifactStagingDirectory)' + -PackageNames $(PackageNames) + -OutBuildOrderFile '$(Build.ArtifactStagingDirectory)/release-order.json' + $(AdditionalPackageParams) + - template: /eng/common/pipelines/templates/steps/publish-1es-artifact.yml parameters: diff --git a/eng/pipelines/templates/jobs/perf.yml b/eng/pipelines/templates/jobs/perf.yml index dfcc6c3610..2be399a3d0 100644 --- a/eng/pipelines/templates/jobs/perf.yml +++ b/eng/pipelines/templates/jobs/perf.yml @@ -1,3 +1,4 @@ + parameters: - name: InstallLanguageSteps type: object diff --git a/eng/pipelines/templates/stages/archetype-rust-release.yml b/eng/pipelines/templates/stages/archetype-rust-release.yml index 5ac10abb01..aaaa2ff318 100644 --- a/eng/pipelines/templates/stages/archetype-rust-release.yml +++ b/eng/pipelines/templates/stages/archetype-rust-release.yml @@ -14,16 +14,13 @@ parameters: - name: DevFeedName type: string default: 'public/azure-sdk-for-rust' -- name: Environment - type: string - default: 'cratesio' stages: - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - ${{ if in(variables['Build.Reason'], 'Manual', '') }}: - - ${{ each artifact in parameters.Artifacts }}: - - stage: Release_${{artifact.safeName}} - displayName: "Release: ${{artifact.name}}" + - ${{ if gt(length(parameters.Artifacts), 0) }}: + - stage: Release_Batch + displayName: "Releasing: ${{length(parameters.Artifacts)}} crates" dependsOn: ${{parameters.DependsOn}} condition: and(succeeded(), ne(variables['SetDevVersion'], 'true'), ne(variables['Skip.Release'], 'true'), ne(variables['Build.Repository.Name'], 'Azure/azure-sdk-for-rust-pr')) variables: @@ -50,16 +47,17 @@ stages: - template: /eng/common/pipelines/templates/steps/retain-run.yml - - script: | - echo "##vso[build.addbuildtag]${{artifact.name}}" - displayName: Add build tag '${{artifact.name}}' + - ${{ each artifact in parameters.Artifacts }}: + - script: | + echo "##vso[build.addbuildtag]${{artifact.name}}" + displayName: Add build tag '${{artifact.name}}' - - template: /eng/common/pipelines/templates/steps/create-tags-and-git-release.yml - parameters: - ArtifactLocation: $(Pipeline.Workspace)/${{parameters.PipelineArtifactName}}/${{artifact.name}} - PackageRepository: Crates.io - ReleaseSha: $(Build.SourceVersion) - WorkingDirectory: $(Pipeline.Workspace)/_work + - template: /eng/common/pipelines/templates/steps/create-tags-and-git-release.yml + parameters: + ArtifactLocation: $(Pipeline.Workspace)/${{parameters.PipelineArtifactName}}/${{artifact.name}} + PackageRepository: Crates.io + ReleaseSha: $(Build.SourceVersion) + WorkingDirectory: $(Pipeline.Workspace)/_work - deployment: PublishPackage displayName: "Publish to Crates.io" @@ -71,7 +69,10 @@ stages: - input: pipelineArtifact # Required, type of the input artifact artifactName: ${{parameters.PipelineArtifactName}} # Required, name of the pipeline artifact targetPath: $(Pipeline.Workspace)/drop # Optional, specifies where the artifact is downloaded to - environment: ${{parameters.Environment}} + ${{if parameters.TestPipeline}}: + environment: none + ${{else}}: + environment: cratesio # This timeout shouldn't be necessary once we're able to parallelize better. Right now, # this is here to ensure larger areas (30+) libraries don't time out. timeoutInMinutes: 120 @@ -84,33 +85,83 @@ stages: runOnce: deploy: steps: - - template: /eng/pipelines/templates/steps/use-rust.yml@self - parameters: - Toolchain: stable - - - pwsh: | - $additionalOwners = @('heaths', 'hallipr') - $token = $env:CARGO_REGISTRY_TOKEN - $crateName = '${{artifact.name}}' - - $manifestPath = "$(Pipeline.Workspace)/drop/$crateName/contents/Cargo.toml" - Write-Host "> cargo publish --manifest-path `"$manifestPath`"" - cargo publish --manifest-path $manifestPath - if (!$?) { - Write-Error "Failed to publish package: '$crateName'" - exit 1 - } - - $existingOwners = (cargo owner --list $crateName) -replace " \(.*", "" - $missingOwners = $additionalOwners | Where-Object { $existingOwners -notcontains $_ } - - foreach ($owner in $missingOwners) { - Write-Host "> cargo owner --add $owner $crateName" - cargo owner --add $owner $crateName - } - displayName: Publish Crate - env: - CARGO_REGISTRY_TOKEN: $(azure-sdk-cratesio-token) + - pwsh: | + Write-Host "##vso[task.setvariable variable=ArtifactIndex]0" + displayName: Set ArtifactIndex to 0 + + - ${{ each artifact in parameters.Artifacts }}: + - pwsh: | + # Read artifact release order from release-order.json + # and use ArtifactIndex to select the right one + $index = [int]'$(ArtifactIndex)' + $artifacts = Get-Content '$(Pipeline.Workspace)/drop/release-order.json' | ConvertFrom-Json + + # Force $artifacts to be an array (PowerShell unrolls single-item arrays) + if ($artifacts -isnot [Array]) { + $artifacts = @($artifacts) + } + + if ($index -ge $artifacts.Count) { + Write-Error "ArtifactIndex $index is out of range (0..$($artifacts.Count - 1))" + exit 1 + } + + $artifactName = $artifacts[$index] + Write-Host "Releasing artifact $artifactName (index $index)" + + $artifactRootPath = '$(Pipeline.Workspace)/drop' + $outDir = '$(Pipeline.Workspace)/esrp-release' + + if (Test-Path $outDir) { + Write-Host "Cleaning output directory: $outDir" + Remove-Item -Path $outDir -Recurse -Force + } + New-Item -ItemType Directory -Path $outDir -Force | Out-Null + + Write-Host "Artifact name: $artifactName" + + $packageMetadataPath = "$artifactRootPath/PackageInfo/$artifactName.json" + if (!(Test-Path $packageMetadataPath)) { + Write-Error "Package metadata file not found: $packageMetadataPath" + exit 1 + } + + $packageMetadata = Get-Content -Raw $packageMetadataPath | ConvertFrom-Json + $packageVersion = $packageMetadata.version + Write-Host "Package version: $packageVersion" + + $cratePath = "$artifactRootPath/$artifactName/$artifactName-$packageVersion.crate" + Copy-Item ` + -Path $cratePath ` + -Destination $outDir + Write-Host "Contents of $outDir" + Get-ChildItem -Path $outDir | ForEach-Object { Write-Host $_.FullName } + displayName: 'Copy crate for ESRP' + + - task: EsrpRelease@10 + displayName: 'ESRP Release' + inputs: + connectedservicename: 'Azure SDK PME Managed Identity' + ClientId: '5f81938c-2544-4f1f-9251-dd9de5b8a81b' + DomainTenantId: '975f013f-7f24-47e8-a7d3-abc4752bf346' + Usemanagedidentity: true + KeyVaultName: 'kv-azuresdk-codesign' + SignCertName: 'azure-sdk-esrp-release-certificate' + intent: 'packagedistribution' + contenttype: 'Rust' + contentsource: 'Folder' + folderlocation: '$(Pipeline.Workspace)/esrp-release' + waitforreleasecompletion: true + owners: ${{ coalesce(variables['Build.RequestedForEmail'], 'azuresdk@microsoft.com') }} + approvers: ${{ coalesce(variables['Build.RequestedForEmail'], 'azuresdk@microsoft.com') }} + serviceendpointurl: 'https://api.esrp.microsoft.com/' + mainpublisher: 'ESRPRELPACMANTEST' + + - pwsh: | + $index = [int]'$(ArtifactIndex)' + 1 + Write-Host "Setting ArtifactIndex to $index" + Write-Host "##vso[task.setvariable variable=ArtifactIndex]$index" + displayName: Increment ArtifactIndex - job: UpdatePackageVersion displayName: "API Review and Package Version Update" @@ -130,69 +181,32 @@ stages: displayName: Download ${{parameters.PipelineArtifactName}} artifact artifact: ${{parameters.PipelineArtifactName}} - - template: /eng/common/pipelines/templates/steps/create-apireview.yml - parameters: - ArtifactPath: $(Pipeline.Workspace)/${{parameters.PipelineArtifactName}} - Artifacts: ${{parameters.Artifacts}} - ConfigFileDir: $(Pipeline.Workspace)/${{parameters.PipelineArtifactName}}/PackageInfo - MarkPackageAsShipped: true - ArtifactName: ${{parameters.PipelineArtifactName}} - SourceRootPath: $(System.DefaultWorkingDirectory) - PackageName: ${{artifact.name}} - - # Apply the version increment to each library, which updates the Cargo.toml and changelog files. - - task: PowerShell@2 - displayName: Increment ${{artifact.name}} version - inputs: - targetType: filePath - filePath: $(Build.SourcesDirectory)/eng/scripts/Update-PackageVersion.ps1 - arguments: > - -ServiceDirectory '${{parameters.ServiceDirectory}}' - -PackageName '${{artifact.name}}' + - ${{each artifact in parameters.Artifacts }}: + - template: /eng/common/pipelines/templates/steps/create-apireview.yml + parameters: + ArtifactPath: $(Pipeline.Workspace)/${{parameters.PipelineArtifactName}} + Artifacts: ${{parameters.Artifacts}} + ConfigFileDir: $(Pipeline.Workspace)/${{parameters.PipelineArtifactName}}/PackageInfo + MarkPackageAsShipped: true + ArtifactName: ${{parameters.PipelineArtifactName}} + SourceRootPath: $(System.DefaultWorkingDirectory) + PackageName: ${{artifact.name}} + + # Apply the version increment to each library, which updates the Cargo.toml and changelog files. + - task: PowerShell@2 + displayName: Increment ${{artifact.name}} version + inputs: + targetType: filePath + filePath: $(Build.SourcesDirectory)/eng/scripts/Update-PackageVersion.ps1 + arguments: > + -ServiceDirectory '${{parameters.ServiceDirectory}}' + -PackageName '${{artifact.name}}' - template: /eng/common/pipelines/templates/steps/create-pull-request.yml parameters: PRBranchName: increment-package-version-${{parameters.ServiceDirectory}}-$(Build.BuildId) - CommitMsg: "Increment package version after release of ${{ artifact.name }}" + CommitMsg: "Increment package version after release of ${{ join(', ', parameters.Artifacts.*.name) }}" PRTitle: "Increment versions for ${{parameters.ServiceDirectory}} releases" CloseAfterOpenForTesting: '${{parameters.TestPipeline}}' ${{ if startsWith(variables['Build.SourceBranch'], 'refs/pull/') }}: BaseBranchName: main - - - ${{ if eq(parameters.TestPipeline, true) }}: - - job: ManualApproval - displayName: "Manual approval" - dependsOn: PublishPackage - condition: ne(variables['Skip.PublishPackage'], 'true') - pool: server - timeoutInMinutes: 120 # 2 hours - steps: - - task: ManualValidation@1 - timeoutInMinutes: 60 # 1 hour - inputs: - notifyUsers: '' # Required, but empty string allowed - allowApproversToApproveTheirOwnRuns: true - instructions: "Approve yank of ${{ artifact.name }}" - onTimeout: 'resume' - - - job: YankCrates - displayName: "Yank Crates" - dependsOn: ManualApproval - condition: and(succeeded(), ne(variables['Skip.PublishPackage'], 'true')) - steps: - - template: /eng/common/pipelines/templates/steps/sparse-checkout.yml - - - download: current - displayName: Download ${{parameters.PipelineArtifactName}} artifact - artifact: ${{parameters.PipelineArtifactName}} - - - task: PowerShell@2 - displayName: Yank Crates - env: - CARGO_REGISTRY_TOKEN: $(azure-sdk-cratesio-token) - inputs: - targetType: filePath - filePath: $(Build.SourcesDirectory)/eng/scripts/Yank-Crates.ps1 - arguments: - -CrateNames '${{artifact.name}}' - -PackageInfoDirectory '$(Pipeline.Workspace)/${{parameters.PipelineArtifactName}}/PackageInfo' diff --git a/eng/pipelines/templates/stages/archetype-sdk-client.yml b/eng/pipelines/templates/stages/archetype-sdk-client.yml index 141ec4de3a..9cd7dd8817 100644 --- a/eng/pipelines/templates/stages/archetype-sdk-client.yml +++ b/eng/pipelines/templates/stages/archetype-sdk-client.yml @@ -148,6 +148,9 @@ extends: parameters: DependsOn: "Build" ServiceDirectory: ${{ parameters.ServiceDirectory }} - Artifacts: ${{ parameters.Artifacts }} + Artifacts: + - ${{ each artifact in parameters.Artifacts }}: + - ${{ if ne(artifact.releaseInBatch, 'false')}}: + - ${{ artifact }} TestPipeline: ${{ eq(parameters.ServiceDirectory, 'canary') }} PipelineArtifactName: packages diff --git a/eng/scripts/Language-Settings.ps1 b/eng/scripts/Language-Settings.ps1 index a973cc38f3..d085a398fc 100644 --- a/eng/scripts/Language-Settings.ps1 +++ b/eng/scripts/Language-Settings.ps1 @@ -1,7 +1,7 @@ $Language = "rust" $LanguageDisplayName = "Rust" $PackageRepository = "crates.io" -$packagePattern = "Cargo.toml" +$packagePattern = "*.crate" #$MetadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/main/_data/releases/latest/rust-packages.csv" $GithubUri = "https://github.com/Azure/azure-sdk-for-rust" $PackageRepositoryUri = "https://crates.io/crates" @@ -139,15 +139,32 @@ function Get-rust-AdditionalValidationPackagesFromPackageSet ($packagesWithChang return $additionalPackages ?? @() } +# $GetPackageInfoFromPackageFileFn = "Get-${Language}-PackageInfoFromPackageFile" function Get-rust-PackageInfoFromPackageFile([IO.FileInfo]$pkg, [string]$workingDirectory) { - #$pkg will be a FileInfo object for the Cargo.toml file in a package artifact directory - $package = cargo read-manifest --manifest-path $pkg.FullName | ConvertFrom-Json + # Create a temporary folder for extraction + $extractionPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) + New-Item -ItemType Directory -Path $extractionPath | Out-Null + + # Extract the .crate file (which is a tarball) to the temporary folder + tar -xvf $pkg.FullName -C $extractionPath + $cargoTomlPath = [System.IO.Path]::Combine($extractionPath, $pkg.BaseName, 'Cargo.toml') + + Write-Host "Reading package info from $cargoTomlPath" + if (!(Test-Path $cargoTomlPath)) { + $message = "The Cargo.toml file was not found in the package artifact at $cargoTomlPath" + LogError $message + throw $message + } + + $package = cargo read-manifest --manifest-path $cargoTomlPath | ConvertFrom-Json $packageName = $package.name $packageVersion = $package.version - $changeLogLoc = Get-ChildItem -Path $pkg.DirectoryName -Filter "CHANGELOG.md" | Select-Object -First 1 - $readmeContentLoc = Get-ChildItem -Path $pkg.DirectoryName -Filter "README.md" | Select-Object -First 1 + $packageAssetPath = [System.IO.Path]::Combine($extractionPath, "$packageName-$packageVersion") + + $changeLogLoc = Get-ChildItem -Path $packageAssetPath -Filter "CHANGELOG.md" | Select-Object -First 1 + $readmeContentLoc = Get-ChildItem -Path $packageAssetPath -Filter "README.md" | Select-Object -First 1 if ($changeLogLoc) { $releaseNotes = Get-ChangeLogEntryAsString -ChangeLogLocation $changeLogLoc -VersionString $packageVersion diff --git a/eng/scripts/Pack-Crates.ps1 b/eng/scripts/Pack-Crates.ps1 index b266667345..0592719f3f 100755 --- a/eng/scripts/Pack-Crates.ps1 +++ b/eng/scripts/Pack-Crates.ps1 @@ -8,22 +8,20 @@ param( [string[]]$PackageNames, [Parameter(ParameterSetName = 'PackageInfo')] [string]$PackageInfoDirectory, - [switch]$NoVerify + [switch]$Release, + [switch]$NoVerify, + [string]$OutBuildOrderFile ) $ErrorActionPreference = 'Stop' -. (Join-Path $PSScriptRoot '..' 'common' 'scripts' 'common.ps1') +. ([System.IO.Path]::Combine($PSScriptRoot, '..', 'common', 'scripts', 'common.ps1')) Write-Host @" Packing crates with RUSTFLAGS: '${env:RUSTFLAGS}' "@ -if ($OutputPath) { - $OutputPath = New-Item -ItemType Directory -Path $OutputPath -Force | Select-Object -ExpandProperty FullName -} - function Get-OutputPackageNames($workspacePackages) { $packablePackages = $workspacePackages | Where-Object -Property publish -NE -Value @() $packablePackageNames = $packablePackages.name @@ -57,15 +55,11 @@ function Get-OutputPackageNames($workspacePackages) { return $names } -function Get-CargoMetadata() { - cargo metadata --no-deps --format-version 1 --manifest-path "$RepoRoot/Cargo.toml" | ConvertFrom-Json -Depth 100 -AsHashtable -} - function Get-CargoPackages() { $metadata = Get-CargoMetadata - # path based depdenencies are assumed to be unreleased package versions - # they must be included in this build and build before packages that depend on them + # Path based dependencies are assumed to be unreleased package versions. In + # non-release builds these should be packed as well. foreach ($package in $metadata.packages) { $package.UnreleasedDependencies = @() foreach ($dependency in $package.dependencies) { @@ -83,9 +77,13 @@ function Get-PackagesToBuild() { $packages = Get-CargoPackages $outputPackageNames = Get-OutputPackageNames $packages - # We start with output packages, then recursively add unreleased dependencies to the list of packages that need to be built [array]$packagesToBuild = $packages | Where-Object { $outputPackageNames.Contains($_.name) } + if ($Release) { + return $packagesToBuild + } + + # If not releasing, expand dependencies into list of packages to build $toProcess = $packagesToBuild while ($toProcess.Length -gt 0) { $package = $toProcess[0] @@ -99,51 +97,11 @@ function Get-PackagesToBuild() { } } - $buildOrder = @() - - # Then we order the packages to that dependencies are built first - while ($packagesToBuild.Count -gt 0) { - # Pick any package with no unreleased dependencies, add it to the build order and remove it from the list of other packages' unreleased dependencies - $package = $packagesToBuild | Where-Object { $_.UnreleasedDependencies.Count -eq 0 } | Select-Object -First 1 - - if (-not $package) { - Write-Error "These packages cannot be built because they depend on unreleased dependencies that aren't being built." -ErrorAction Continue - foreach ($package in $packagesToBuild) { - Write-Error " $($package.name) -> $($package.UnreleasedDependencies -join ', ')" -ErrorAction Continue - } - exit 1 - } - - $package.OutputPackage = $outputPackageNames.Contains($package.name) - $buildOrder += $package - $packagesToBuild = @($packagesToBuild -ne $package) - - foreach ($otherPackage in $packagesToBuild) { - $otherPackage.UnreleasedDependencies = $otherPackage.UnreleasedDependencies -ne $package - } - } - - return $buildOrder -} - -function Initialize-VendorDirectory() { - $path = "$RepoRoot/target/vendor" - Invoke-LoggedCommand "cargo vendor $path" -GroupOutput | Out-Host - return $path + return $packagesToBuild } -function Add-CrateToLocalRegistry($LocalRegistryPath, $Package) { - $packageName = $Package.name - $packageVersion = $Package.version - - # create an index entry for the package - $packagePath = "$RepoRoot/target/package/$packageName-$packageVersion" - - Write-Host "Copying package '$packageName' to vendor directory '$LocalRegistryPath'" - Copy-Item -Path $packagePath -Destination $LocalRegistryPath -Recurse - - #write an empty checksum file - '{"files":{}}' | Out-File -FilePath "$LocalRegistryPath/$packageName-$packageVersion/.cargo-checksum.json" -Encoding utf8 +function Get-CargoMetadata() { + cargo metadata --no-deps --format-version 1 --manifest-path "$RepoRoot/Cargo.toml" | ConvertFrom-Json -Depth 100 -AsHashtable } function Create-ApiViewFile($package) { @@ -156,64 +114,67 @@ function Create-ApiViewFile($package) { "$packagePath/review/$packageName.rust.json" } -Push-Location $RepoRoot +$originalLocation = Get-Location try { - $localRegistryPath = Initialize-VendorDirectory + Set-Location $RepoRoot [array]$packages = Get-PackagesToBuild - - Write-Host "Building packages in the following order:" + $packageParams = @() foreach ($package in $packages) { - $packageName = $package.name - $type = if ($package.OutputPackage) { "output" } else { "dependency" } - Write-Host " $packageName ($type)" + $packageParams += "--package", $package.name } - foreach ($package in $packages) { - Write-Host "" - - $packageName = $package.name - $packageVersion = $package.version - - $command = "cargo publish --locked --dry-run --package $packageName --registry crates-io --config `"source.crates-io.replace-with='local'`" --config `"source.local.directory='$localRegistryPath'`" --allow-dirty" - - if ($NoVerify) { - $command += " --no-verify" - } - - Invoke-LoggedCommand -Command $command -GroupOutput + if ($NoVerify) { + $packageParams += "--no-verify" + } + Write-Host "> cargo publish --locked --dry-run --allow-dirty $($packageParams -join ' ')" + & cargo publish --locked --dry-run --allow-dirty @packageParams 2>&1 | Tee-Object -Variable packResult + if ($LASTEXITCODE) { + Write-Host "cargo publish failed with exit code $LASTEXITCODE" + exit $LASTEXITCODE + } - # copy the package to the local registry - Add-CrateToLocalRegistry ` - -LocalRegistryPath $localRegistryPath ` - -Package $package + if ($OutputPath) { + $OutputPath = New-Item -ItemType Directory -Path $OutputPath -Force | Select-Object -ExpandProperty FullName - if ($OutputPath -and $package.OutputPackage) { - $sourcePath = "$RepoRoot/target/package/$packageName-$packageVersion" - $targetPath = "$OutputPath/$packageName" - $targetContentsPath = "$targetPath/contents" - $targetApiReviewFile = "$targetPath/$packageName.rust.json" + foreach ($package in $packages) { + $sourcePath = [System.IO.Path]::Combine($RepoRoot, "target", "package", "$($package.name)-$($package.version)") + $targetPath = [System.IO.Path]::Combine($OutputPath, $package.name) + $targetContentsPath = [System.IO.Path]::Combine($targetPath, "contents") + $targetApiReviewFile = [System.IO.Path]::Combine($targetPath, "$($package.name).rust.json") if (Test-Path -Path $targetContentsPath) { Remove-Item -Path $targetContentsPath -Recurse -Force } - Write-Host "Copying package '$packageName' to '$targetContentsPath'" + Write-Host "Copying package contents '$($package.name)' to '$targetContentsPath'" New-Item -ItemType Directory -Path $targetContentsPath -Force | Out-Null - Copy-Item -Path $sourcePath/* -Destination $targetContentsPath -Recurse -Exclude "Cargo.toml.orig" + Copy-Item -Path $sourcePath/* -Destination $targetContentsPath -Recurse + + Write-Host "Copying .crate file for '$($package.name)' to '$targetPath'" + Copy-Item -Path "$sourcePath.crate" -Destination $targetPath -Force Write-Host "Creating API review file" $apiReviewFile = Create-ApiViewFile $package - + Write-Host "Copying API review file to '$targetApiReviewFile'" Copy-Item -Path $apiReviewFile -Destination $targetApiReviewFile -Force } } - Write-Host "Removing local registry" - Remove-Item -Path $localRegistryPath -Recurse -Force | Out-Null + if ($OutBuildOrderFile) { + $buildOrder = @() + foreach ($line in $packResult) { + if ($line -match '^\s*Packaging (\w*) ([\w\d\.-]*)') { + $buildOrder += $matches[1] + } + } + + Write-Host "Build Order: $($buildOrder -join ', ')" + $buildOrder | ConvertTo-Json -Depth 100 | Set-Content $OutBuildOrderFile + } } finally { - Pop-Location + Set-Location $originalLocation } diff --git a/sdk/canary/azure_canary_core/Cargo.toml b/sdk/canary/azure_canary_core/Cargo.toml index 18c2d595a9..156a62dab6 100644 --- a/sdk/canary/azure_canary_core/Cargo.toml +++ b/sdk/canary/azure_canary_core/Cargo.toml @@ -9,6 +9,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] +azure_core = { workspace = true } [dev-dependencies] azure_canary.path = "../azure_canary" diff --git a/sdk/canary/ci.yml b/sdk/canary/ci.yml index 0ffe09337e..55e0c848fd 100644 --- a/sdk/canary/ci.yml +++ b/sdk/canary/ci.yml @@ -10,12 +10,22 @@ trigger: include: - sdk/canary/ +parameters: +- name: release_azure_canary_core + displayName: azure_canary_core + type: boolean + default: false +- name: release_azure_canary + displayName: azure_canary + type: boolean + default: false + extends: template: /eng/pipelines/templates/stages/archetype-sdk-client.yml parameters: ServiceDirectory: canary Artifacts: - - name: azure_canary_core - safeName: AzureTemplateCore - name: azure_canary - safeName: AzureTemplate + releaseInBatch: ${{ parameters.release_azure_canary }} + - name: azure_canary_core + releaseInBatch: ${{ parameters.release_azure_canary_core }} diff --git a/sdk/core/ci.yml b/sdk/core/ci.yml index d9b219bf49..c7dc313f70 100644 --- a/sdk/core/ci.yml +++ b/sdk/core/ci.yml @@ -10,16 +10,52 @@ trigger: include: - sdk/core/ +parameters: +- name: typespec + displayName: typespec + type: boolean + default: false +- name: typespec_macros + displayName: typespec_macros + type: boolean + default: false +- name: typespec_client_core + displayName: typespec_client_core + type: boolean + default: false +- name: release_azure_core + displayName: azure_core + type: boolean + default: false +- name: release_azure_core_macros + displayName: azure_core_macros + type: boolean + default: false +- name: release_azure_core_amqp + displayName: azure_core_amqp + type: boolean + default: false +- name: release_azure_core_opentelemetry + displayName: azure_core_opentelemetry + type: boolean + default: false + extends: template: /eng/pipelines/templates/stages/archetype-sdk-client.yml parameters: ServiceDirectory: core Artifacts: + - name: typespec + releaseInBatch: ${{ parameters.typespec }} + - name: typespec_macros + releaseInBatch: ${{ parameters.typespec_macros }} + - name: typespec_client_core + releaseInBatch: ${{ parameters.typespec_client_core }} - name: azure_core - safeName: AzureCore + releaseInBatch: ${{ parameters.azure_core }} - name: azure_core_macros - safeName: AzureCoreMacros + releaseInBatch: ${{ parameters.azure_core_macros }} - name: azure_core_amqp - safeName: AzureCoreAmqp + releaseInBatch: ${{ parameters.azure_core_amqp }} - name: azure_core_opentelemetry - safeName: AzureCoreOpentelemetry + releaseInBatch: ${{ parameters.azure_core_opentelemetry }}