From 04d95af6ee18ea384e5a6cbdae4e5f934b320fb3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 19:59:46 +0000 Subject: [PATCH 1/9] Initial plan From 5881cf2a385c297c1986fdcefd46cccc46797b45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:12:47 +0000 Subject: [PATCH 2/9] Publish generator dev versions during RegenPreview so emitter deps are restorable in CI Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../eng/pipeline/publish.yml | 6 + .../eng/scripts/RegenPreview.psm1 | 305 +++++++++++++++--- .../eng/scripts/Submit-AzureSdkForNetPr.ps1 | 71 ++-- 3 files changed, 306 insertions(+), 76 deletions(-) diff --git a/packages/http-client-csharp/eng/pipeline/publish.yml b/packages/http-client-csharp/eng/pipeline/publish.yml index e37e84583fb..f356a2fb64f 100644 --- a/packages/http-client-csharp/eng/pipeline/publish.yml +++ b/packages/http-client-csharp/eng/pipeline/publish.yml @@ -24,6 +24,11 @@ parameters: type: boolean default: false + - name: PublishGeneratorPackages + displayName: Publish locally built generator packages so the azure-sdk-for-net PR references published versions (enables CI) + type: boolean + default: false + - name: RunTests displayName: Run unit tests type: boolean @@ -248,6 +253,7 @@ extends: ${{ replace(replace('True', eq(variables['Build.SourceBranchName'], 'main'), ''), 'True', '-Internal') }} ${{ replace(replace('True', eq(parameters.RegenerateAzureLibraries, false), ''), 'True', '-RegenerateAzureLibraries') }} ${{ replace(replace('True', eq(parameters.RegenerateMgmtLibraries, false), ''), 'True', '-RegenerateMgmtLibraries') }} + ${{ replace(replace('True', eq(parameters.PublishGeneratorPackages, false), ''), 'True', '-PublishGeneratorPackages') }} -BuildArtifactsPath '$(Pipeline.Workspace)/build_artifacts_csharp/packages' -UseTypeSpecNext:$${{ parameters.UseTypeSpecNext }} diff --git a/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 b/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 index d23204e8fe9..27f2ca2a864 100644 --- a/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 +++ b/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 @@ -1,5 +1,168 @@ Import-Module "$PSScriptRoot\Generation.psm1" -DisableNameChecking -Force -Global +# Default npm registry used for resolving and publishing generator preview packages. +# This is the public azure-sdk-for-js Azure Artifacts feed. +$script:DefaultGeneratorRegistry = "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/" + +function Publish-GeneratorPackage { + <# + .SYNOPSIS + Publishes a locally built generator package (.tgz) to an npm registry. + + .DESCRIPTION + Publishing the locally built generator packages to the configured registry allows the + regenerated emitter-package.json artifacts to reference a published version instead of a + host-only "file:" path. That way the resulting azure-sdk-for-net PR can restore the emitter + dependencies in CI. + + Re-publishing a version that already exists on the feed is treated as success so the operation + is idempotent across pipeline re-runs. + + .PARAMETER PackagePath + Path to the packed .tgz file to publish. + + .PARAMETER Registry + The npm registry to publish to. + + .PARAMETER NpmrcPath + Optional path to an .npmrc file (with authentication) to use for the publish operation. + #> + param( + [Parameter(Mandatory=$true)] + [string]$PackagePath, + + [Parameter(Mandatory=$true)] + [string]$Registry, + + [Parameter(Mandatory=$false)] + [string]$NpmrcPath + ) + + if (-not (Test-Path $PackagePath)) { + throw "Package not found for publishing: $PackagePath" + } + + Write-Host "Publishing $(Split-Path $PackagePath -Leaf) to $Registry..." -ForegroundColor Gray + + $publishArgs = @("publish", $PackagePath, "--registry", $Registry) + if ($NpmrcPath -and (Test-Path $NpmrcPath)) { + $publishArgs += @("--userconfig", $NpmrcPath) + } + + $publishOutput = & npm @publishArgs 2>&1 + if ($LASTEXITCODE -ne 0) { + $outputText = ($publishOutput | Out-String) + # Treat an already-published version as success so re-runs remain idempotent. + if ($outputText -match "EPUBLISHCONFLICT" -or + $outputText -match "cannot publish over" -or + $outputText -match "previously published" -or + $outputText -match "409 Conflict") { + Write-Host " Package version already published; continuing." -ForegroundColor Yellow + return + } + Write-Host $outputText -ForegroundColor Red + throw "Failed to publish package: $PackagePath" + } + + Write-Host " Published $(Split-Path $PackagePath -Leaf)" -ForegroundColor Green +} + +function Update-EmitterPackageArtifact { + <# + .SYNOPSIS + Updates an eng-folder emitter-package.json (and its lock file) to reference a generator package. + + .DESCRIPTION + By default the emitter dependency is pinned to the locally built .tgz using a "file:" path (used + for local validation loops that clean up afterwards). When a publish registry and version are + provided, the dependency is instead pinned to the published version resolved from the registry, so + the committed artifacts can be restored by CI in the resulting azure-sdk-for-net PR. + + .PARAMETER EmitterJsonPath + Path to the emitter-package.json file to update. + + .PARAMETER LockJsonPath + Path to the emitter-package-lock.json file to update. + + .PARAMETER PackagePath + Path to the locally built .tgz package (used for the default "file:" mode). + + .PARAMETER PackageName + The npm package name to reference (required for published mode). + + .PARAMETER PublishVersion + The published version to pin to (when publishing). + + .PARAMETER Registry + The npm registry to resolve the published package from. + #> + param( + [Parameter(Mandatory=$true)] + [string]$EmitterJsonPath, + + [Parameter(Mandatory=$true)] + [string]$LockJsonPath, + + [Parameter(Mandatory=$false)] + [string]$PackagePath, + + [Parameter(Mandatory=$false)] + [string]$PackageName, + + [Parameter(Mandatory=$false)] + [string]$PublishVersion, + + [Parameter(Mandatory=$false)] + [string]$Registry = $script:DefaultGeneratorRegistry + ) + + $ErrorActionPreference = 'Stop' + + $usePublishedVersion = [bool]$PublishVersion + if ($usePublishedVersion -and -not $PackageName) { + throw "PackageName is required when PublishVersion is specified." + } + if (-not $usePublishedVersion -and -not $PackagePath) { + throw "PackagePath is required when PublishVersion is not specified." + } + + $tempDir = Join-Path ([System.IO.Path]::GetDirectoryName($EmitterJsonPath)) "temp-emitter-package-update-$([System.Guid]::NewGuid().ToString('N'))" + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + + # Point the default registry at the public feed so dependency resolution does not fall back to + # the authenticated machine-global proxy, which fails with E401 for @typespec/@azure-tools packages. + Set-Content (Join-Path $tempDir ".npmrc") "registry=$Registry`n" -Encoding utf8 + + try { + $tempPackageJson = Join-Path $tempDir "package.json" + Copy-Item $EmitterJsonPath $tempPackageJson -Force + + Push-Location $tempDir + try { + if ($usePublishedVersion) { + Invoke "npm install $PackageName@$PublishVersion --save-exact --package-lock-only --registry $Registry" $tempDir + } else { + Invoke "npm install `"`"file:$PackagePath`"`" --package-lock-only" $tempDir + } + if ($LASTEXITCODE -ne 0) { + throw "Failed to update emitter package artifacts for $EmitterJsonPath" + } + + Copy-Item $tempPackageJson $EmitterJsonPath -Force + $lockFile = Join-Path $tempDir "package-lock.json" + if (Test-Path $lockFile) { + Copy-Item $lockFile $LockJsonPath -Force + } + } + finally { + Pop-Location + } + } + finally { + Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue + } +} + function Update-GeneratorPackage { <# .SYNOPSIS @@ -33,6 +196,14 @@ function Update-GeneratorPackage { .PARAMETER UseNpmCi If true, runs 'npm install --package-lock-only && npm ci' instead of 'npm install'. + + .PARAMETER PublishRegistry + When provided, dependencies are pinned to the published version ($PublishVersion) instead of a + local "file:" path, and the packed .tgz is published to this registry so the resulting emitter + artifacts reference a version that CI can restore. + + .PARAMETER NpmrcPath + Optional path to an authenticated .npmrc file used when publishing to $PublishRegistry. #> param( [Parameter(Mandatory=$true)] @@ -51,11 +222,21 @@ function Update-GeneratorPackage { [string]$DebugFolder, [Parameter(Mandatory=$false)] - [bool]$UseNpmCi = $false + [bool]$UseNpmCi = $false, + + [Parameter(Mandatory=$false)] + [string]$PublishRegistry, + + [Parameter(Mandatory=$false)] + [string]$NpmrcPath ) $ErrorActionPreference = 'Stop' + # When publishing, dependencies must reference the published version so the packed (and published) + # package does not carry host-only "file:" references that consumers cannot restore. + $usePublishedDependencies = [bool]$PublishRegistry + $packageJsonPath = Join-Path $GeneratorPath "package.json" $originalPackageJson = Get-Content $packageJsonPath -Raw @@ -67,18 +248,30 @@ function Update-GeneratorPackage { foreach ($dep in $Dependencies.GetEnumerator()) { if ($packageJson.dependencies -and $packageJson.dependencies.PSObject.Properties[$dep.Key]) { - $packageJson.dependencies.($dep.Key) = "file:$($dep.Value)" + if ($usePublishedDependencies) { + $packageJson.dependencies.($dep.Key) = $LocalVersion + } else { + $packageJson.dependencies.($dep.Key) = "file:$($dep.Value)" + } } } foreach ($dep in $DevDependencies.GetEnumerator()) { if ($packageJson.devDependencies -and $packageJson.devDependencies.PSObject.Properties[$dep.Key]) { - $packageJson.devDependencies.($dep.Key) = "file:$($dep.Value)" + if ($usePublishedDependencies) { + $packageJson.devDependencies.($dep.Key) = $LocalVersion + } else { + $packageJson.devDependencies.($dep.Key) = "file:$($dep.Value)" + } } } $packageJson | ConvertTo-Json -Depth 100 | Set-Content $packageJsonPath -Encoding UTF8 - Write-Host " Updated dependencies to local packages" -ForegroundColor Green + if ($usePublishedDependencies) { + Write-Host " Updated dependencies to published version $LocalVersion" -ForegroundColor Green + } else { + Write-Host " Updated dependencies to local packages" -ForegroundColor Green + } } # Step 2: Install dependencies, clean, and build. @@ -156,7 +349,13 @@ function Update-GeneratorPackage { Move-Item $sourcePath $destPath -Force Write-Host " Package created: $packageFile" -ForegroundColor Green - + + # Step 4 (optional): Publish the package so downstream emitter artifacts can reference a + # restorable published version instead of a host-only "file:" path. + if ($PublishRegistry) { + Publish-GeneratorPackage -PackagePath $destPath -Registry $PublishRegistry -NpmrcPath $NpmrcPath + } + return $destPath } finally { @@ -198,6 +397,14 @@ function Update-MgmtGenerator { .PARAMETER LocalVersion The version string to use for the local package (e.g., "1.0.0-alpha.20250127.abc123"). + + .PARAMETER PublishRegistry + When provided, the mgmt (and its local dependencies) are pinned to the published version and the + packed mgmt package is published to this registry. The emitter-package.json artifact then + references the published version instead of a host-only "file:" path so CI can restore it. + + .PARAMETER NpmrcPath + Optional path to an authenticated .npmrc file used when publishing to $PublishRegistry. #> param( [Parameter(Mandatory=$true)] @@ -207,7 +414,13 @@ function Update-MgmtGenerator { [string]$DebugFolder, [Parameter(Mandatory=$true)] - [string]$LocalVersion + [string]$LocalVersion, + + [Parameter(Mandatory=$false)] + [string]$PublishRegistry, + + [Parameter(Mandatory=$false)] + [string]$NpmrcPath ) $ErrorActionPreference = 'Stop' @@ -247,52 +460,32 @@ function Update-MgmtGenerator { } ` -LocalVersion $LocalVersion ` -DebugFolder $DebugFolder ` - -UseNpmCi $false + -UseNpmCi $false ` + -PublishRegistry $PublishRegistry ` + -NpmrcPath $NpmrcPath # Update eng folder mgmt emitter package artifacts. # The emitter package only declares @azure-typespec/http-client-csharp-mgmt directly; - # the Azure + unbranded packages are pulled in transitively from the mgmt tgz, which - # now points at the local file: packages. Installing the local mgmt tgz regenerates - # the lock file with the full local dependency graph. + # the Azure + unbranded packages are pulled in transitively from the mgmt package. When + # publishing, the emitter references the published mgmt version so CI can restore it; + # otherwise it points at the local mgmt tgz for host-only validation loops. Write-Host "Updating mgmt emitter package artifacts..." -ForegroundColor Gray - + $mgmtEmitterJson = Join-Path $EngFolder "azure-typespec-http-client-csharp-mgmt-emitter-package.json" - - # Regenerate the package-lock.json with the full (local) dependency graph - $tempDir = Join-Path $EngFolder "temp-mgmt-package-update" - New-Item -ItemType Directory -Path $tempDir -Force | Out-Null - - # Point the default registry at the public azure-sdk-for-js feed so dependency - # resolution doesn't fall back to the authenticated machine-global proxy - # (packagefeedproxy), which fails with E401 for @typespec/@azure-tools packages. - Set-Content (Join-Path $tempDir ".npmrc") "registry=https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/`n" -Encoding utf8 - - try { - $tempPackageJson = Join-Path $tempDir "package.json" - - Copy-Item $mgmtEmitterJson $tempPackageJson -Force - - Push-Location $tempDir - try { - # Install the mgmt package and regenerate lock file with both dependencies - Invoke "npm install `"`"file:$mgmtPackagePath`"`" --package-lock-only" $tempDir - - Copy-Item $tempPackageJson $mgmtEmitterJson -Force - $lockFile = Join-Path $tempDir "package-lock.json" - if (Test-Path $lockFile) { - $mgmtLockJson = Join-Path $EngFolder "azure-typespec-http-client-csharp-mgmt-emitter-package-lock.json" - Copy-Item $lockFile $mgmtLockJson -Force - } - - Write-Host " Mgmt emitter package artifacts updated" -ForegroundColor Green - } - finally { - Pop-Location - } + $mgmtLockJson = Join-Path $EngFolder "azure-typespec-http-client-csharp-mgmt-emitter-package-lock.json" + + $updateEmitterArgs = @{ + EmitterJsonPath = $mgmtEmitterJson + LockJsonPath = $mgmtLockJson + PackagePath = $mgmtPackagePath } - finally { - Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue + if ($PublishRegistry) { + $updateEmitterArgs.PackageName = '@azure-typespec/http-client-csharp-mgmt' + $updateEmitterArgs.PublishVersion = $LocalVersion + $updateEmitterArgs.Registry = $PublishRegistry } + Update-EmitterPackageArtifact @updateEmitterArgs + Write-Host " Mgmt emitter package artifacts updated" -ForegroundColor Green Write-Host "" @@ -329,6 +522,14 @@ function Update-AzureGenerator { .PARAMETER LocalVersion The version string to use for the local package (e.g., "1.0.0-alpha.20250127.abc123"). + + .PARAMETER PublishRegistry + When provided, the Azure generator is pinned to the published unbranded version and the packed + Azure package is published to this registry so downstream emitter artifacts can reference a + restorable published version instead of a host-only "file:" path. + + .PARAMETER NpmrcPath + Optional path to an authenticated .npmrc file used when publishing to $PublishRegistry. #> param( [Parameter(Mandatory=$true)] @@ -344,7 +545,13 @@ function Update-AzureGenerator { [string]$PackagesDataPropsPath, [Parameter(Mandatory=$true)] - [string]$LocalVersion + [string]$LocalVersion, + + [Parameter(Mandatory=$false)] + [string]$PublishRegistry, + + [Parameter(Mandatory=$false)] + [string]$NpmrcPath ) $ErrorActionPreference = 'Stop' @@ -360,7 +567,9 @@ function Update-AzureGenerator { -Dependencies @{ '@typespec/http-client-csharp' = $UnbrandedPackagePath } ` -LocalVersion $LocalVersion ` -DebugFolder $DebugFolder ` - -UseNpmCi $true + -UseNpmCi $true ` + -PublishRegistry $PublishRegistry ` + -NpmrcPath $NpmrcPath # Build and package Azure.Generator NuGet package Write-Host "Packing Azure.Generator NuGet package..." -ForegroundColor Gray @@ -978,4 +1187,4 @@ function Update-AzureSpectorScenarios { return $generationOutput } -Export-ModuleMember -Function "Update-MgmtGenerator", "Update-AzureGenerator", "Filter-LibrariesByGenerator", "Update-OpenAIGenerator", "Add-LocalNuGetSource", "Update-AzureSpectorScenarios" +Export-ModuleMember -Function "Update-MgmtGenerator", "Update-AzureGenerator", "Filter-LibrariesByGenerator", "Update-OpenAIGenerator", "Add-LocalNuGetSource", "Update-AzureSpectorScenarios", "Publish-GeneratorPackage", "Update-EmitterPackageArtifact" diff --git a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 index 0ed2f4152e4..0ca23b36f1e 100755 --- a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 +++ b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 @@ -21,6 +21,8 @@ When specified, builds the management plane emitter locally and regenerates mgmt Path to the build artifacts directory containing the published .tgz and .nupkg files. Required when RegenerateAzureLibraries or RegenerateMgmtLibraries is specified. .PARAMETER PipelineRunUrl The URL of the pipeline run that triggered this PR. When provided, it is included in the PR description for traceability. +.PARAMETER PublishGeneratorPackages +When specified (together with RegenerateAzureLibraries or RegenerateMgmtLibraries), the locally built Azure/mgmt generator packages are published to the npm registry configured in the .npmrc, and the regenerated emitter-package.json artifacts reference the published version instead of a host-only "file:" path. This allows CI to restore the emitter dependencies in the resulting azure-sdk-for-net PR. #> [CmdletBinding(SupportsShouldProcess = $true)] param( @@ -54,6 +56,9 @@ param( [Parameter(Mandatory = $false)] [string]$PipelineRunUrl, + [Parameter(Mandatory = $false)] + [switch]$PublishGeneratorPackages, + [Parameter(Mandatory = $false)] [switch]$UseTypeSpecNext ) @@ -63,6 +68,22 @@ Import-Module (Join-Path $PSScriptRoot "Generation.psm1") -DisableNameChecking - # Import RegenPreview module for Update-AzureGenerator and Update-MgmtGenerator Import-Module (Join-Path $PSScriptRoot "RegenPreview.psm1") -DisableNameChecking -Force +# When publishing generator packages, resolve the registry and authenticated .npmrc used for publishing. +# The emitter artifacts will then reference the published version instead of a host-only "file:" path, +# allowing CI to restore the emitter dependencies in the resulting azure-sdk-for-net PR. +$PublishRegistry = $null +$PublishNpmrcPath = $null +if ($PublishGeneratorPackages) { + $PublishRegistry = "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/" + $resolvedPublishNpmrc = Join-Path $PSScriptRoot "../../.npmrc" + if (Test-Path $resolvedPublishNpmrc) { + $PublishNpmrcPath = (Resolve-Path $resolvedPublishNpmrc).Path + Write-Host "Generator packages will be published to $PublishRegistry using .npmrc at $PublishNpmrcPath" + } else { + Write-Host "Generator packages will be published to $PublishRegistry using ambient npm configuration" + } +} + # Set up variables for the PR $RepoOwner = "Azure" $RepoName = "azure-sdk-for-net" @@ -483,38 +504,30 @@ try { -UnbrandedPackagePath $unbrandedPackagePath ` -DebugFolder $debugFolder ` -PackagesDataPropsPath $packagesDataPropsPath ` - -LocalVersion $PackageVersion + -LocalVersion $PackageVersion ` + -PublishRegistry $PublishRegistry ` + -NpmrcPath $PublishNpmrcPath Write-Host "Azure generator built successfully" - # Update Azure emitter package artifacts + # Update Azure emitter package artifacts. When publishing, reference the published + # version so CI can restore it; otherwise pin to the local tgz via a "file:" path. Write-Host "Updating Azure emitter package artifacts..." $engFolder = Join-Path $tempDir "eng" - $azureTempDir = Join-Path $engFolder "temp-azure-package-update" - New-Item -ItemType Directory -Path $azureTempDir -Force | Out-Null - - try { - $azureEmitterJson = Join-Path $engFolder "azure-typespec-http-client-csharp-emitter-package.json" - $tempPackageJson = Join-Path $azureTempDir "package.json" - - Copy-Item $azureEmitterJson $tempPackageJson -Force - - Push-Location $azureTempDir - try { - Invoke "npm install `"`"file:$azurePackagePath`"`" --package-lock-only" $azureTempDir - - Copy-Item $tempPackageJson $azureEmitterJson -Force - $lockFile = Join-Path $azureTempDir "package-lock.json" - if (Test-Path $lockFile) { - $azureLockJson = Join-Path $engFolder "azure-typespec-http-client-csharp-emitter-package-lock.json" - Copy-Item $lockFile $azureLockJson -Force - } - } finally { - Pop-Location - } - } finally { - Remove-Item $azureTempDir -Recurse -Force -ErrorAction SilentlyContinue + $azureEmitterJson = Join-Path $engFolder "azure-typespec-http-client-csharp-emitter-package.json" + $azureLockJson = Join-Path $engFolder "azure-typespec-http-client-csharp-emitter-package-lock.json" + + $updateAzureEmitterArgs = @{ + EmitterJsonPath = $azureEmitterJson + LockJsonPath = $azureLockJson + PackagePath = $azurePackagePath } - + if ($PublishRegistry) { + $updateAzureEmitterArgs.PackageName = '@azure-typespec/http-client-csharp' + $updateAzureEmitterArgs.PublishVersion = $PackageVersion + $updateAzureEmitterArgs.Registry = $PublishRegistry + } + Update-EmitterPackageArtifact @updateAzureEmitterArgs + if ($RegenerateAzureLibraries) { $emitterPatterns += "eng/azure-typespec-http-client-csharp-emitter-package.json" } @@ -543,7 +556,9 @@ try { Update-MgmtGenerator ` -EngFolder $engFolder ` -DebugFolder $debugFolder ` - -LocalVersion $PackageVersion + -LocalVersion $PackageVersion ` + -PublishRegistry $PublishRegistry ` + -NpmrcPath $PublishNpmrcPath Write-Host "Management plane generator built successfully" $emitterPatterns += "eng/azure-typespec-http-client-csharp-mgmt-emitter-package.json" From 896b437b83bafb8cf2ef0635e7e104f3c3c4eacd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:34:36 +0000 Subject: [PATCH 3/9] Publish generator packages by default when a regen is requested (remove opt-in parameter) Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../http-client-csharp/eng/pipeline/publish.yml | 6 ------ .../eng/scripts/Submit-AzureSdkForNetPr.ps1 | 14 +++++--------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/http-client-csharp/eng/pipeline/publish.yml b/packages/http-client-csharp/eng/pipeline/publish.yml index f356a2fb64f..e37e84583fb 100644 --- a/packages/http-client-csharp/eng/pipeline/publish.yml +++ b/packages/http-client-csharp/eng/pipeline/publish.yml @@ -24,11 +24,6 @@ parameters: type: boolean default: false - - name: PublishGeneratorPackages - displayName: Publish locally built generator packages so the azure-sdk-for-net PR references published versions (enables CI) - type: boolean - default: false - - name: RunTests displayName: Run unit tests type: boolean @@ -253,7 +248,6 @@ extends: ${{ replace(replace('True', eq(variables['Build.SourceBranchName'], 'main'), ''), 'True', '-Internal') }} ${{ replace(replace('True', eq(parameters.RegenerateAzureLibraries, false), ''), 'True', '-RegenerateAzureLibraries') }} ${{ replace(replace('True', eq(parameters.RegenerateMgmtLibraries, false), ''), 'True', '-RegenerateMgmtLibraries') }} - ${{ replace(replace('True', eq(parameters.PublishGeneratorPackages, false), ''), 'True', '-PublishGeneratorPackages') }} -BuildArtifactsPath '$(Pipeline.Workspace)/build_artifacts_csharp/packages' -UseTypeSpecNext:$${{ parameters.UseTypeSpecNext }} diff --git a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 index 0ca23b36f1e..cedf37a311d 100755 --- a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 +++ b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 @@ -21,8 +21,6 @@ When specified, builds the management plane emitter locally and regenerates mgmt Path to the build artifacts directory containing the published .tgz and .nupkg files. Required when RegenerateAzureLibraries or RegenerateMgmtLibraries is specified. .PARAMETER PipelineRunUrl The URL of the pipeline run that triggered this PR. When provided, it is included in the PR description for traceability. -.PARAMETER PublishGeneratorPackages -When specified (together with RegenerateAzureLibraries or RegenerateMgmtLibraries), the locally built Azure/mgmt generator packages are published to the npm registry configured in the .npmrc, and the regenerated emitter-package.json artifacts reference the published version instead of a host-only "file:" path. This allows CI to restore the emitter dependencies in the resulting azure-sdk-for-net PR. #> [CmdletBinding(SupportsShouldProcess = $true)] param( @@ -56,9 +54,6 @@ param( [Parameter(Mandatory = $false)] [string]$PipelineRunUrl, - [Parameter(Mandatory = $false)] - [switch]$PublishGeneratorPackages, - [Parameter(Mandatory = $false)] [switch]$UseTypeSpecNext ) @@ -68,12 +63,13 @@ Import-Module (Join-Path $PSScriptRoot "Generation.psm1") -DisableNameChecking - # Import RegenPreview module for Update-AzureGenerator and Update-MgmtGenerator Import-Module (Join-Path $PSScriptRoot "RegenPreview.psm1") -DisableNameChecking -Force -# When publishing generator packages, resolve the registry and authenticated .npmrc used for publishing. -# The emitter artifacts will then reference the published version instead of a host-only "file:" path, -# allowing CI to restore the emitter dependencies in the resulting azure-sdk-for-net PR. +# Publishing the locally built generator packages happens by default whenever a regeneration is +# requested, so the regenerated emitter-package.json artifacts reference a published version instead +# of a host-only "file:" path. That way CI can restore the emitter dependencies in the resulting +# azure-sdk-for-net PR. Resolve the registry and authenticated .npmrc used for publishing. $PublishRegistry = $null $PublishNpmrcPath = $null -if ($PublishGeneratorPackages) { +if ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries) { $PublishRegistry = "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/" $resolvedPublishNpmrc = Join-Path $PSScriptRoot "../../.npmrc" if (Test-Path $resolvedPublishNpmrc) { From c645ad5fabc737ca0b5dcd118d16dd32fce48352 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:02:20 +0000 Subject: [PATCH 4/9] Query ADO feed for next available branded/mgmt emitter versions; publish to ADO feed only Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../eng/scripts/RegenPreview.psm1 | 175 ++++++++++++++++-- .../eng/scripts/Submit-AzureSdkForNetPr.ps1 | 27 ++- 2 files changed, 189 insertions(+), 13 deletions(-) diff --git a/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 b/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 index 27f2ca2a864..3b6ddef3fee 100644 --- a/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 +++ b/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 @@ -67,6 +67,88 @@ function Publish-GeneratorPackage { Write-Host " Published $(Split-Path $PackagePath -Leaf)" -ForegroundColor Green } +function Get-NextGeneratorVersion { + <# + .SYNOPSIS + Computes the next available prerelease version for a generator package by querying the ADO feed. + + .DESCRIPTION + The branded (@azure-typespec/http-client-csharp) and mgmt (@azure-typespec/http-client-csharp-mgmt) + emitters are published to the Azure DevOps npm feed with a date-stamped alpha version following the + existing format "-alpha..". To avoid publish conflicts (which npm feeds reject + because versions are immutable), this queries the feed for the versions already published today and + returns the next available counter. When nothing is published yet for the package (or today), the + counter starts at 1. + + .PARAMETER PackageName + The npm package name to query (e.g. "@azure-typespec/http-client-csharp"). + + .PARAMETER BaseVersion + The base "X.Y.Z" version (without prerelease suffix) taken from the package's package.json. + + .PARAMETER Registry + The ADO npm feed to query. + + .PARAMETER NpmrcPath + Optional path to an authenticated .npmrc file used to query the feed. + #> + param( + [Parameter(Mandatory=$true)] + [string]$PackageName, + + [Parameter(Mandatory=$true)] + [string]$BaseVersion, + + [Parameter(Mandatory=$true)] + [string]$Registry, + + [Parameter(Mandatory=$false)] + [string]$NpmrcPath + ) + + $viewArgs = @("view", $PackageName, "versions", "--json", "--registry", $Registry) + if ($NpmrcPath -and (Test-Path $NpmrcPath)) { + $viewArgs += @("--userconfig", $NpmrcPath) + } + + Write-Host "Querying $Registry for published versions of $PackageName..." -ForegroundColor Gray + $viewOutput = & npm @viewArgs 2>&1 + $publishedVersions = @() + if ($LASTEXITCODE -eq 0) { + try { + $parsed = ($viewOutput | Out-String).Trim() | ConvertFrom-Json + if ($null -ne $parsed) { + # `npm view` returns a bare string when only a single version exists, otherwise an array. + $publishedVersions = @($parsed) + } + } + catch { + $publishedVersions = @() + } + } + elseif (($viewOutput | Out-String) -notmatch "E404") { + # A 404 simply means the package has never been published; any other failure is unexpected. + Write-Host ($viewOutput | Out-String) -ForegroundColor Yellow + throw "Failed to query published versions of $PackageName from $Registry" + } + + $dateStamp = Get-Date -Format 'yyyyMMdd' + $prefix = "$BaseVersion-alpha.$dateStamp" + $maxCounter = 0 + foreach ($version in $publishedVersions) { + if ($version -match ("^" + [regex]::Escape($prefix) + "\.(\d+)$")) { + $counter = [int]$Matches[1] + if ($counter -gt $maxCounter) { + $maxCounter = $counter + } + } + } + + $nextVersion = "$prefix.$($maxCounter + 1)" + Write-Host " Next available version for ${PackageName}: $nextVersion" -ForegroundColor Green + return $nextVersion +} + function Update-EmitterPackageArtifact { <# .SYNOPSIS @@ -198,9 +280,14 @@ function Update-GeneratorPackage { If true, runs 'npm install --package-lock-only && npm ci' instead of 'npm install'. .PARAMETER PublishRegistry - When provided, dependencies are pinned to the published version ($PublishVersion) instead of a - local "file:" path, and the packed .tgz is published to this registry so the resulting emitter - artifacts reference a version that CI can restore. + When provided, dependencies are pinned to their published versions (see PublishedDependencyVersions) + instead of a local "file:" path, and the packed .tgz is published to this registry so the resulting + emitter artifacts reference a version that CI can restore. + + .PARAMETER PublishedDependencyVersions + Hashtable of dependency name -> published version string. Used in publish mode to pin each dependency + to the exact version already published to the feed. Dependencies not listed here fall back to + $LocalVersion for backward compatibility. .PARAMETER NpmrcPath Optional path to an authenticated .npmrc file used when publishing to $PublishRegistry. @@ -227,6 +314,9 @@ function Update-GeneratorPackage { [Parameter(Mandatory=$false)] [string]$PublishRegistry, + [Parameter(Mandatory=$false)] + [hashtable]$PublishedDependencyVersions = @{}, + [Parameter(Mandatory=$false)] [string]$NpmrcPath ) @@ -237,6 +327,16 @@ function Update-GeneratorPackage { # package does not carry host-only "file:" references that consumers cannot restore. $usePublishedDependencies = [bool]$PublishRegistry + # Resolves the published version to pin a dependency to: the explicit per-dependency version when + # provided, otherwise the package's own version (backward-compatible default). + $resolvePublishedVersion = { + param($DependencyName) + if ($PublishedDependencyVersions.ContainsKey($DependencyName)) { + return $PublishedDependencyVersions[$DependencyName] + } + return $LocalVersion + } + $packageJsonPath = Join-Path $GeneratorPath "package.json" $originalPackageJson = Get-Content $packageJsonPath -Raw @@ -249,7 +349,7 @@ function Update-GeneratorPackage { foreach ($dep in $Dependencies.GetEnumerator()) { if ($packageJson.dependencies -and $packageJson.dependencies.PSObject.Properties[$dep.Key]) { if ($usePublishedDependencies) { - $packageJson.dependencies.($dep.Key) = $LocalVersion + $packageJson.dependencies.($dep.Key) = (& $resolvePublishedVersion $dep.Key) } else { $packageJson.dependencies.($dep.Key) = "file:$($dep.Value)" } @@ -259,7 +359,7 @@ function Update-GeneratorPackage { foreach ($dep in $DevDependencies.GetEnumerator()) { if ($packageJson.devDependencies -and $packageJson.devDependencies.PSObject.Properties[$dep.Key]) { if ($usePublishedDependencies) { - $packageJson.devDependencies.($dep.Key) = $LocalVersion + $packageJson.devDependencies.($dep.Key) = (& $resolvePublishedVersion $dep.Key) } else { $packageJson.devDependencies.($dep.Key) = "file:$($dep.Value)" } @@ -268,7 +368,7 @@ function Update-GeneratorPackage { $packageJson | ConvertTo-Json -Depth 100 | Set-Content $packageJsonPath -Encoding UTF8 if ($usePublishedDependencies) { - Write-Host " Updated dependencies to published version $LocalVersion" -ForegroundColor Green + Write-Host " Updated dependencies to published versions" -ForegroundColor Green } else { Write-Host " Updated dependencies to local packages" -ForegroundColor Green } @@ -403,6 +503,18 @@ function Update-MgmtGenerator { packed mgmt package is published to this registry. The emitter-package.json artifact then references the published version instead of a host-only "file:" path so CI can restore it. + .PARAMETER PublishVersion + In publish mode, the version to stamp and publish the mgmt npm emitter package with (typically the + next available version queried from the ADO feed). Defaults to $LocalVersion when not provided. + + .PARAMETER AzureVersion + The published version of the Azure emitter dependency (@azure-typespec/http-client-csharp). Used to + locate the packed Azure .tgz and to pin the mgmt dependency in publish mode. Defaults to $LocalVersion. + + .PARAMETER UnbrandedVersion + The published version of the unbranded emitter dependency (@typespec/http-client-csharp). Used to + locate the packed unbranded .tgz and to pin the mgmt dependency in publish mode. Defaults to $LocalVersion. + .PARAMETER NpmrcPath Optional path to an authenticated .npmrc file used when publishing to $PublishRegistry. #> @@ -419,18 +531,34 @@ function Update-MgmtGenerator { [Parameter(Mandatory=$false)] [string]$PublishRegistry, + [Parameter(Mandatory=$false)] + [string]$PublishVersion, + + [Parameter(Mandatory=$false)] + [string]$AzureVersion, + + [Parameter(Mandatory=$false)] + [string]$UnbrandedVersion, + [Parameter(Mandatory=$false)] [string]$NpmrcPath ) $ErrorActionPreference = 'Stop' + # In publish mode each package carries its own (feed-queried) version, so the Azure and unbranded + # dependencies must be located and pinned by their respective published versions. When not publishing, + # all three share $LocalVersion for the host-only "file:" validation loop. + $mgmtNpmVersion = if ($PublishVersion) { $PublishVersion } else { $LocalVersion } + $azureVer = if ($AzureVersion) { $AzureVersion } else { $LocalVersion } + $unbrandedVer = if ($UnbrandedVersion) { $UnbrandedVersion } else { $LocalVersion } + # Derive all paths from EngFolder $mgmtGeneratorPath = Join-Path $EngFolder "packages" "http-client-csharp-mgmt" # Package paths come from debug folder - $azurePackageName = "azure-typespec-http-client-csharp-$LocalVersion.tgz" - $unbrandedPackageName = "typespec-http-client-csharp-$LocalVersion.tgz" + $azurePackageName = "azure-typespec-http-client-csharp-$azureVer.tgz" + $unbrandedPackageName = "typespec-http-client-csharp-$unbrandedVer.tgz" $azurePackagePath = Join-Path $DebugFolder $azurePackageName $unbrandedPackagePath = Join-Path $DebugFolder $unbrandedPackageName @@ -445,6 +573,9 @@ function Update-MgmtGenerator { Write-Host "Azure package: $azurePackagePath" -ForegroundColor Gray Write-Host "Unbranded package: $unbrandedPackagePath" -ForegroundColor Gray Write-Host "Local version: $LocalVersion" -ForegroundColor Gray + if ($PublishRegistry) { + Write-Host "Mgmt emitter publish version: $mgmtNpmVersion" -ForegroundColor Gray + } Write-Host "" # Use shared helper to build and package the mgmt generator. @@ -458,10 +589,14 @@ function Update-MgmtGenerator { '@azure-typespec/http-client-csharp' = $azurePackagePath '@typespec/http-client-csharp' = $unbrandedPackagePath } ` - -LocalVersion $LocalVersion ` + -LocalVersion $mgmtNpmVersion ` -DebugFolder $DebugFolder ` -UseNpmCi $false ` -PublishRegistry $PublishRegistry ` + -PublishedDependencyVersions @{ + '@azure-typespec/http-client-csharp' = $azureVer + '@typespec/http-client-csharp' = $unbrandedVer + } ` -NpmrcPath $NpmrcPath # Update eng folder mgmt emitter package artifacts. @@ -481,7 +616,7 @@ function Update-MgmtGenerator { } if ($PublishRegistry) { $updateEmitterArgs.PackageName = '@azure-typespec/http-client-csharp-mgmt' - $updateEmitterArgs.PublishVersion = $LocalVersion + $updateEmitterArgs.PublishVersion = $mgmtNpmVersion $updateEmitterArgs.Registry = $PublishRegistry } Update-EmitterPackageArtifact @updateEmitterArgs @@ -528,6 +663,10 @@ function Update-AzureGenerator { Azure package is published to this registry so downstream emitter artifacts can reference a restorable published version instead of a host-only "file:" path. + .PARAMETER PublishVersion + In publish mode, the version to stamp and publish the Azure npm emitter package with (typically the + next available version queried from the ADO feed). Defaults to $LocalVersion when not provided. + .PARAMETER NpmrcPath Optional path to an authenticated .npmrc file used when publishing to $PublishRegistry. #> @@ -550,25 +689,37 @@ function Update-AzureGenerator { [Parameter(Mandatory=$false)] [string]$PublishRegistry, + [Parameter(Mandatory=$false)] + [string]$PublishVersion, + [Parameter(Mandatory=$false)] [string]$NpmrcPath ) $ErrorActionPreference = 'Stop' + # In publish mode the Azure npm emitter is stamped with its own (feed-queried) version, while its + # unbranded dependency is pinned to the already-published unbranded version ($LocalVersion). When not + # publishing, the package keeps $LocalVersion for the host-only "file:" validation loop. + $azureNpmVersion = if ($PublishVersion) { $PublishVersion } else { $LocalVersion } + Write-Host "Azure generator path: $AzureGeneratorPath" -ForegroundColor Gray Write-Host "Unbranded package: $UnbrandedPackagePath" -ForegroundColor Gray Write-Host "Local version: $LocalVersion" -ForegroundColor Gray + if ($PublishRegistry) { + Write-Host "Azure emitter publish version: $azureNpmVersion" -ForegroundColor Gray + } Write-Host "" # Use shared helper to build and package the Azure generator $azurePackagePath = Update-GeneratorPackage ` -GeneratorPath $AzureGeneratorPath ` -Dependencies @{ '@typespec/http-client-csharp' = $UnbrandedPackagePath } ` - -LocalVersion $LocalVersion ` + -LocalVersion $azureNpmVersion ` -DebugFolder $DebugFolder ` -UseNpmCi $true ` -PublishRegistry $PublishRegistry ` + -PublishedDependencyVersions @{ '@typespec/http-client-csharp' = $LocalVersion } ` -NpmrcPath $NpmrcPath # Build and package Azure.Generator NuGet package @@ -1187,4 +1338,4 @@ function Update-AzureSpectorScenarios { return $generationOutput } -Export-ModuleMember -Function "Update-MgmtGenerator", "Update-AzureGenerator", "Filter-LibrariesByGenerator", "Update-OpenAIGenerator", "Add-LocalNuGetSource", "Update-AzureSpectorScenarios", "Publish-GeneratorPackage", "Update-EmitterPackageArtifact" +Export-ModuleMember -Function "Update-MgmtGenerator", "Update-AzureGenerator", "Filter-LibrariesByGenerator", "Update-OpenAIGenerator", "Add-LocalNuGetSource", "Update-AzureSpectorScenarios", "Publish-GeneratorPackage", "Update-EmitterPackageArtifact", "Get-NextGeneratorVersion" diff --git a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 index cedf37a311d..0b04a22f87d 100755 --- a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 +++ b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 @@ -494,7 +494,18 @@ try { Write-Host "##[section]Building Azure generator..." $previousErrorAction = $ErrorActionPreference $ErrorActionPreference = "Continue" + # When publishing, query the ADO feed for the next available version to stamp the Azure + # emitter package with, following the existing "-alpha.." format. + $azurePublishVersion = $null try { + if ($PublishRegistry) { + $azureBaseVersion = ((Get-Content (Join-Path $azureGeneratorPath "package.json") -Raw | ConvertFrom-Json).version -split '-')[0] + $azurePublishVersion = Get-NextGeneratorVersion ` + -PackageName '@azure-typespec/http-client-csharp' ` + -BaseVersion $azureBaseVersion ` + -Registry $PublishRegistry ` + -NpmrcPath $PublishNpmrcPath + } $azurePackagePath = Update-AzureGenerator ` -AzureGeneratorPath $azureGeneratorPath ` -UnbrandedPackagePath $unbrandedPackagePath ` @@ -502,6 +513,7 @@ try { -PackagesDataPropsPath $packagesDataPropsPath ` -LocalVersion $PackageVersion ` -PublishRegistry $PublishRegistry ` + -PublishVersion $azurePublishVersion ` -NpmrcPath $PublishNpmrcPath Write-Host "Azure generator built successfully" @@ -519,7 +531,7 @@ try { } if ($PublishRegistry) { $updateAzureEmitterArgs.PackageName = '@azure-typespec/http-client-csharp' - $updateAzureEmitterArgs.PublishVersion = $PackageVersion + $updateAzureEmitterArgs.PublishVersion = $azurePublishVersion $updateAzureEmitterArgs.Registry = $PublishRegistry } Update-EmitterPackageArtifact @updateAzureEmitterArgs @@ -549,11 +561,24 @@ try { $ErrorActionPreference = "Continue" try { $engFolder = Join-Path $tempDir "eng" + # When publishing, query the ADO feed for the next available mgmt emitter version. + $mgmtPublishVersion = $null + if ($PublishRegistry) { + $mgmtBaseVersion = ((Get-Content (Join-Path $mgmtGeneratorPath "package.json") -Raw | ConvertFrom-Json).version -split '-')[0] + $mgmtPublishVersion = Get-NextGeneratorVersion ` + -PackageName '@azure-typespec/http-client-csharp-mgmt' ` + -BaseVersion $mgmtBaseVersion ` + -Registry $PublishRegistry ` + -NpmrcPath $PublishNpmrcPath + } Update-MgmtGenerator ` -EngFolder $engFolder ` -DebugFolder $debugFolder ` -LocalVersion $PackageVersion ` -PublishRegistry $PublishRegistry ` + -PublishVersion $mgmtPublishVersion ` + -AzureVersion $azurePublishVersion ` + -UnbrandedVersion $PackageVersion ` -NpmrcPath $PublishNpmrcPath Write-Host "Management plane generator built successfully" From 878aca271f417c887f6069273556a04a47b4e7b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:03:31 +0000 Subject: [PATCH 5/9] Hoist version-counter regex out of loop in Get-NextGeneratorVersion Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- packages/http-client-csharp/eng/scripts/RegenPreview.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 b/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 index 3b6ddef3fee..3c5c3d01050 100644 --- a/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 +++ b/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 @@ -134,9 +134,10 @@ function Get-NextGeneratorVersion { $dateStamp = Get-Date -Format 'yyyyMMdd' $prefix = "$BaseVersion-alpha.$dateStamp" + $counterPattern = "^" + [regex]::Escape($prefix) + "\.(\d+)$" $maxCounter = 0 foreach ($version in $publishedVersions) { - if ($version -match ("^" + [regex]::Escape($prefix) + "\.(\d+)$")) { + if ($version -match $counterPattern) { $counter = [int]$Matches[1] if ($counter -gt $maxCounter) { $maxCounter = $counter From 0d34d47ba4d3e106bfaeb540df66620f5d1c6715 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:27:04 +0000 Subject: [PATCH 6/9] Orchestrate generator build/publish/regen as discrete steps in publish.yml Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../eng/pipeline/publish.yml | 61 ++- .../eng/scripts/Submit-AzureSdkForNetPr.ps1 | 452 ++++++++++-------- 2 files changed, 316 insertions(+), 197 deletions(-) diff --git a/packages/http-client-csharp/eng/pipeline/publish.yml b/packages/http-client-csharp/eng/pipeline/publish.yml index e37e84583fb..1dfb1f4a8e5 100644 --- a/packages/http-client-csharp/eng/pipeline/publish.yml +++ b/packages/http-client-csharp/eng/pipeline/publish.yml @@ -123,6 +123,9 @@ extends: TMPDIR: $(Agent.TempDirectory) NUGET_PACKAGES: $(Agent.TempDirectory)/nuget npm_config_cache: $(Agent.TempDirectory)/npm-cache + # Shared on-disk state for the discrete Prepare / Publish generators / Regenerate steps. + AzureSdkForNetCheckout: $(Agent.TempDirectory)/azure-sdk-for-net + GeneratorPackagesFolder: $(Agent.TempDirectory)/generator-packages steps: - checkout: self @@ -232,14 +235,70 @@ extends: TokenOwners: - Azure + # The azure-sdk-for-net PR is produced by three discrete, independently-failing steps that + # share a single on-disk checkout ($(AzureSdkForNetCheckout)) and generator package folder + # ($(GeneratorPackagesFolder)): + # 1. Prepare - clone azure-sdk-for-net and regenerate the unbranded emitter artifacts. + # 2. Publish generators - build the Azure (and mgmt) generator from the published unbranded + # generator artifact and publish it to the ADO feed. Only runs when a + # regen was requested, and fails the pipeline on any build/publish error. + # 3. Regenerate & PR - regenerate the SDK libraries and open the azure-sdk-for-net PR. - task: AzureCLI@2 - displayName: Generate emitter-package.json files & create PR in azure-sdk-for-net + displayName: "Prepare azure-sdk-for-net checkout & regenerate unbranded emitter" inputs: azureSubscription: "AzureSDKEngKeyVault Secrets" scriptType: pscore scriptLocation: scriptPath scriptPath: $(Build.SourcesDirectory)/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 arguments: > + -Phase 'Prepare' + -WorkingDirectory '$(AzureSdkForNetCheckout)' + -DebugFolder '$(GeneratorPackagesFolder)' + -PackageVersion '$(PackageVersion)' + -TypeSpecCommitUrl '$(TypeSpecCommitUrl)' + -AuthToken '$(GH_TOKEN)' + -TypeSpecSourcePackageJsonPath '$(Build.SourcesDirectory)/packages/http-client-csharp/package.json' + -PipelineRunUrl '$(PipelineRunUrl)' + ${{ replace(replace('True', eq(variables['Build.SourceBranchName'], 'main'), ''), 'True', '-Internal') }} + ${{ replace(replace('True', eq(parameters.RegenerateAzureLibraries, false), ''), 'True', '-RegenerateAzureLibraries') }} + ${{ replace(replace('True', eq(parameters.RegenerateMgmtLibraries, false), ''), 'True', '-RegenerateMgmtLibraries') }} + -BuildArtifactsPath '$(Pipeline.Workspace)/build_artifacts_csharp/packages' + -UseTypeSpecNext:$${{ parameters.UseTypeSpecNext }} + + - task: AzureCLI@2 + displayName: "Build & publish Azure/mgmt generators to ADO feed" + condition: and(succeeded(), or(${{ parameters.RegenerateAzureLibraries }}, ${{ parameters.RegenerateMgmtLibraries }})) + inputs: + azureSubscription: "AzureSDKEngKeyVault Secrets" + scriptType: pscore + scriptLocation: scriptPath + scriptPath: $(Build.SourcesDirectory)/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 + arguments: > + -Phase 'PublishGenerators' + -WorkingDirectory '$(AzureSdkForNetCheckout)' + -DebugFolder '$(GeneratorPackagesFolder)' + -PackageVersion '$(PackageVersion)' + -TypeSpecCommitUrl '$(TypeSpecCommitUrl)' + -AuthToken '$(GH_TOKEN)' + -TypeSpecSourcePackageJsonPath '$(Build.SourcesDirectory)/packages/http-client-csharp/package.json' + -PipelineRunUrl '$(PipelineRunUrl)' + ${{ replace(replace('True', eq(variables['Build.SourceBranchName'], 'main'), ''), 'True', '-Internal') }} + ${{ replace(replace('True', eq(parameters.RegenerateAzureLibraries, false), ''), 'True', '-RegenerateAzureLibraries') }} + ${{ replace(replace('True', eq(parameters.RegenerateMgmtLibraries, false), ''), 'True', '-RegenerateMgmtLibraries') }} + -BuildArtifactsPath '$(Pipeline.Workspace)/build_artifacts_csharp/packages' + -UseTypeSpecNext:$${{ parameters.UseTypeSpecNext }} + + - task: AzureCLI@2 + displayName: "Regenerate SDK libraries & create PR in azure-sdk-for-net" + inputs: + azureSubscription: "AzureSDKEngKeyVault Secrets" + scriptType: pscore + scriptLocation: scriptPath + scriptPath: $(Build.SourcesDirectory)/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 + arguments: > + -Phase 'Regenerate' + -WorkingDirectory '$(AzureSdkForNetCheckout)' + -DebugFolder '$(GeneratorPackagesFolder)' -PackageVersion '$(PackageVersion)' -TypeSpecCommitUrl '$(TypeSpecCommitUrl)' -AuthToken '$(GH_TOKEN)' diff --git a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 index 0b04a22f87d..35005e63b96 100755 --- a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 +++ b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 @@ -21,6 +21,16 @@ When specified, builds the management plane emitter locally and regenerates mgmt Path to the build artifacts directory containing the published .tgz and .nupkg files. Required when RegenerateAzureLibraries or RegenerateMgmtLibraries is specified. .PARAMETER PipelineRunUrl The URL of the pipeline run that triggered this PR. When provided, it is included in the PR description for traceability. +.PARAMETER Phase +Which phase of the flow to run. The publish pipeline drives these as discrete, independently-failing steps that share a single on-disk checkout: + - Prepare: Clone azure-sdk-for-net, update the generator version, regenerate the unbranded test projects and emitter-package.json artifacts. + - PublishGenerators: Build the Azure (and mgmt) generator from the published unbranded generator artifact and publish it to the ADO feed, pinning the emitter artifacts to the published version. + - Regenerate: Regenerate the SDK libraries and open the azure-sdk-for-net PR. +Defaults to 'All', which runs every phase in a single invocation (used for local, non-pipeline runs). +.PARAMETER WorkingDirectory +The azure-sdk-for-net checkout directory shared across phases. When omitted (typically for the 'All' phase), a unique temp directory is created and cleaned up automatically. +.PARAMETER DebugFolder +The directory holding the packed generator packages and NuGet packages shared between the PublishGenerators and Regenerate phases. When omitted, a unique temp directory is derived from WorkingDirectory. #> [CmdletBinding(SupportsShouldProcess = $true)] param( @@ -55,7 +65,17 @@ param( [string]$PipelineRunUrl, [Parameter(Mandatory = $false)] - [switch]$UseTypeSpecNext + [switch]$UseTypeSpecNext, + + [Parameter(Mandatory = $false)] + [ValidateSet('All', 'Prepare', 'PublishGenerators', 'Regenerate')] + [string]$Phase = 'All', + + [Parameter(Mandatory = $false)] + [string]$WorkingDirectory, + + [Parameter(Mandatory = $false)] + [string]$DebugFolder ) # Import the Generation module to use the Invoke helper function @@ -80,6 +100,12 @@ if ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries) { } } +# The publish pipeline drives this script as three discrete, independently-failing steps that share a +# single on-disk azure-sdk-for-net checkout (see publish.yml). Resolve which phases this invocation runs. +$runPrepare = $Phase -in @('All', 'Prepare') +$runPublishGenerators = $Phase -in @('All', 'PublishGenerators') +$runRegenerate = $Phase -in @('All', 'Regenerate') + # Set up variables for the PR $RepoOwner = "Azure" $RepoName = "azure-sdk-for-net" @@ -143,22 +169,49 @@ Write-Host "Creating PR in $RepoOwner/$RepoName" Write-Host "Branch: $PRBranch" Write-Host "Title: $PRTitle" -# Create temp folder for repo -$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "azure-sdk-for-net-$(Get-Date -Format 'yyyyMMdd-HHmmss')" -New-Item -ItemType Directory -Path $tempDir -Force | Out-Null -Write-Host "Created temp directory: $tempDir" +# Resolve the shared checkout directory. The publish pipeline passes an explicit, deterministic +# -WorkingDirectory so the Prepare / PublishGenerators / Regenerate steps operate on the same on-disk +# checkout. For a single 'All' invocation (e.g. local runs) fall back to a unique temp directory. +if ($WorkingDirectory) { + $tempDir = $WorkingDirectory +} else { + $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "azure-sdk-for-net-$(Get-Date -Format 'yyyyMMdd-HHmmss')" +} -try { - # Use sparse checkout to clone only the necessary files - # This significantly reduces disk space usage as azure-sdk-for-net is a very large repository - Write-Host "Setting up sparse checkout for azure-sdk-for-net repository..." - - # Initialize empty git repository - git init $tempDir - if ($LASTEXITCODE -ne 0) { - throw "Failed to initialize repository" +# The debug folder holds the packed generator + NuGet packages shared between the PublishGenerators and +# Regenerate phases. Derive a deterministic default from the checkout so both phases agree. +if (-not $DebugFolder) { + $DebugFolder = Join-Path $tempDir ".generator-packages" +} + +if ($runPrepare) { + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + Write-Host "Created temp directory: $tempDir" +} elseif (-not (Test-Path $tempDir)) { + throw "Working directory '$tempDir' does not exist. The Prepare phase must run before the '$Phase' phase." +} + +# Derived paths shared across phases (each phase may run in a separate process, so compute these from +# $tempDir rather than relying on state set by an earlier phase). +$propsFilePath = Join-Path $tempDir "eng/centralpackagemanagement/Directory.Generation.Packages.props" +$packageJsonPath = Join-Path $tempDir "eng/packages/http-client-csharp/package.json" +$httpClientDir = Join-Path $tempDir "eng/packages/http-client-csharp" + +# Whether the unbranded npm install/build succeeded during Prepare. In a split pipeline run the +# Regenerate phase only runs when the Prepare step succeeded (the pipeline fails otherwise), so default +# to true; the 'All' path updates this in the Prepare section below. The Prepare phase persists the +# resolved value to a sibling state file so the later phase processes can honor the same gating. +$installSucceeded = $true +$prepareStateFile = "$tempDir.prepare-state.json" +if (-not $runPrepare -and (Test-Path $prepareStateFile)) { + try { + $installSucceeded = [bool]((Get-Content $prepareStateFile -Raw | ConvertFrom-Json).InstallSucceeded) + } catch { + Write-Warning "Failed to read Prepare state from ${prepareStateFile}: $($_.Exception.Message). Assuming install succeeded." } - +} + +try { Push-Location $tempDir # Set the authentication token for gh CLI early so that scripts invoked @@ -169,47 +222,60 @@ try { # Configure git user for commits in this repository git config user.name "azure-sdk" git config user.email "azuresdk@microsoft.com" - - # Add the remote - git remote add origin "https://github.com/$RepoOwner/$RepoName.git" - if ($LASTEXITCODE -ne 0) { - throw "Failed to add remote" - } - - # Enable sparse checkout with cone mode for better performance - git sparse-checkout init --cone - if ($LASTEXITCODE -ne 0) { - throw "Failed to initialize sparse checkout" - } - - # Set the sparse checkout patterns - only the directories we need - # Note: 'eng' covers eng/packages/http-client-csharp, eng/packages/http-client-csharp-mgmt, and all eng/ artifacts - # Note: 'doc/GeneratorVersions' is needed for regenerating the emitter version dashboard - git sparse-checkout set eng sdk/core/Azure.Core/src/Shared sdk/core/Azure.Core.TestFramework/src doc/GeneratorVersions - if ($LASTEXITCODE -ne 0) { - throw "Failed to set sparse checkout patterns" - } - - # Fetch only the main branch with depth 1 - Write-Host "Fetching $BaseBranch branch with sparse checkout..." - git fetch --depth 1 origin $BaseBranch - if ($LASTEXITCODE -ne 0) { - throw "Failed to fetch repository" - } - - # Checkout the fetched branch - git checkout $BaseBranch - if ($LASTEXITCODE -ne 0) { - throw "Failed to checkout $BaseBranch" - } - # Create a new branch - Write-Host "Creating branch $PRBranch..." - git checkout -b $PRBranch - if ($LASTEXITCODE -ne 0) { - throw "Failed to create branch" + if ($runPrepare) { + # Use sparse checkout to clone only the necessary files + # This significantly reduces disk space usage as azure-sdk-for-net is a very large repository + Write-Host "Setting up sparse checkout for azure-sdk-for-net repository..." + + # Initialize empty git repository + git init $tempDir + if ($LASTEXITCODE -ne 0) { + throw "Failed to initialize repository" + } + + # Add the remote + git remote add origin "https://github.com/$RepoOwner/$RepoName.git" + if ($LASTEXITCODE -ne 0) { + throw "Failed to add remote" + } + + # Enable sparse checkout with cone mode for better performance + git sparse-checkout init --cone + if ($LASTEXITCODE -ne 0) { + throw "Failed to initialize sparse checkout" + } + + # Set the sparse checkout patterns - only the directories we need + # Note: 'eng' covers eng/packages/http-client-csharp, eng/packages/http-client-csharp-mgmt, and all eng/ artifacts + # Note: 'doc/GeneratorVersions' is needed for regenerating the emitter version dashboard + git sparse-checkout set eng sdk/core/Azure.Core/src/Shared sdk/core/Azure.Core.TestFramework/src doc/GeneratorVersions + if ($LASTEXITCODE -ne 0) { + throw "Failed to set sparse checkout patterns" + } + + # Fetch only the main branch with depth 1 + Write-Host "Fetching $BaseBranch branch with sparse checkout..." + git fetch --depth 1 origin $BaseBranch + if ($LASTEXITCODE -ne 0) { + throw "Failed to fetch repository" + } + + # Checkout the fetched branch + git checkout $BaseBranch + if ($LASTEXITCODE -ne 0) { + throw "Failed to checkout $BaseBranch" + } + + # Create a new branch + Write-Host "Creating branch $PRBranch..." + git checkout -b $PRBranch + if ($LASTEXITCODE -ne 0) { + throw "Failed to create branch" + } } + if ($runPrepare) { # Update the dependency in eng/centralpackagemanagement/Directory.Generation.Packages.props Write-Host "Updating dependency version in eng/centralpackagemanagement/Directory.Generation.Packages.props..." $propsFilePath = Join-Path $tempDir "eng/centralpackagemanagement/Directory.Generation.Packages.props" @@ -446,155 +512,144 @@ try { } else { Write-Warning "TypeSpecSourcePackageJsonPath not provided or file doesn't exist. Skipping emitter-package.json generation." } - - # Regenerate all SDK libraries that use the unbranded emitter - if ($installSucceeded) { + + # Persist the resolved install state so the later PublishGenerators / Regenerate phases (which may + # run as separate processes) honor the same gating as a single 'All' invocation. + @{ InstallSucceeded = $installSucceeded } | ConvertTo-Json | Set-Content $prepareStateFile -Encoding utf8 + } # end if ($runPrepare) + + # PublishGenerators phase: build the Azure (and mgmt) generator from the published unbranded + # generator artifact and publish it to the ADO feed, pinning the emitter artifacts to the published + # version. This runs as a discrete, fail-hard pipeline step (see publish.yml) so any build/publish + # failure fails the pipeline instead of silently producing an unrestorable PR. + if ($runPublishGenerators -and $installSucceeded -and ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries)) { + $regenScope = @() + if ($RegenerateAzureLibraries) { $regenScope += "Azure data plane" } + if ($RegenerateMgmtLibraries) { $regenScope += "mgmt" } + Write-Host "##[section]Building and publishing generators for: $($regenScope -join ', ')..." + + # Locate the unbranded .tgz and .nupkg files from build artifacts + if (-not $BuildArtifactsPath -or -not (Test-Path $BuildArtifactsPath)) { + throw "BuildArtifactsPath is required when RegenerateAzureLibraries or RegenerateMgmtLibraries is specified. Path: $BuildArtifactsPath" + } + + New-Item -ItemType Directory -Path $DebugFolder -Force | Out-Null + + # Find unbranded .tgz from build artifacts + $unbrandedTgz = Get-ChildItem -Path $BuildArtifactsPath -Filter "typespec-http-client-csharp-*.tgz" -Recurse | Select-Object -First 1 + if (-not $unbrandedTgz) { + throw "Could not find unbranded emitter .tgz in build artifacts at: $BuildArtifactsPath" + } + $unbrandedPackagePath = $unbrandedTgz.FullName + Write-Host "Using unbranded package from build artifacts: $unbrandedPackagePath" + + # Copy .nupkg files from build artifacts to debug folder + $nupkgFiles = Get-ChildItem -Path $BuildArtifactsPath -Filter "*.nupkg" -Recurse + foreach ($nupkg in $nupkgFiles) { + Copy-Item $nupkg.FullName -Destination $DebugFolder -Force + Write-Host "Copied NuGet package: $($nupkg.Name)" + } + + # Build and package Azure generator (needed for both Azure data plane and mgmt) + $azureGeneratorPath = Join-Path $tempDir "eng" "packages" "http-client-csharp" + $packagesDataPropsPath = Join-Path $tempDir "eng" "centralpackagemanagement" "Directory.Generation.Packages.props" + $engFolder = Join-Path $tempDir "eng" + + Write-Host "##[section]Building Azure generator..." + # When publishing, query the ADO feed for the next available version to stamp the Azure + # emitter package with, following the existing "-alpha.." format. + $azurePublishVersion = $null + if ($PublishRegistry) { + $azureBaseVersion = ((Get-Content (Join-Path $azureGeneratorPath "package.json") -Raw | ConvertFrom-Json).version -split '-')[0] + $azurePublishVersion = Get-NextGeneratorVersion ` + -PackageName '@azure-typespec/http-client-csharp' ` + -BaseVersion $azureBaseVersion ` + -Registry $PublishRegistry ` + -NpmrcPath $PublishNpmrcPath + } + $azurePackagePath = Update-AzureGenerator ` + -AzureGeneratorPath $azureGeneratorPath ` + -UnbrandedPackagePath $unbrandedPackagePath ` + -DebugFolder $DebugFolder ` + -PackagesDataPropsPath $packagesDataPropsPath ` + -LocalVersion $PackageVersion ` + -PublishRegistry $PublishRegistry ` + -PublishVersion $azurePublishVersion ` + -NpmrcPath $PublishNpmrcPath + Write-Host "Azure generator built successfully" + + # Update Azure emitter package artifacts. When publishing, reference the published + # version so CI can restore it; otherwise pin to the local tgz via a "file:" path. + Write-Host "Updating Azure emitter package artifacts..." + $azureEmitterJson = Join-Path $engFolder "azure-typespec-http-client-csharp-emitter-package.json" + $azureLockJson = Join-Path $engFolder "azure-typespec-http-client-csharp-emitter-package-lock.json" + + $updateAzureEmitterArgs = @{ + EmitterJsonPath = $azureEmitterJson + LockJsonPath = $azureLockJson + PackagePath = $azurePackagePath + } + if ($PublishRegistry) { + $updateAzureEmitterArgs.PackageName = '@azure-typespec/http-client-csharp' + $updateAzureEmitterArgs.PublishVersion = $azurePublishVersion + $updateAzureEmitterArgs.Registry = $PublishRegistry + } + Update-EmitterPackageArtifact @updateAzureEmitterArgs + + # Add NuGet source for local packages + $nugetConfigPath = Join-Path $tempDir "NuGet.Config" + if (Test-Path $nugetConfigPath) { + Add-LocalNuGetSource -NuGetConfigPath $nugetConfigPath -SourcePath $DebugFolder + } + + # Build and package management plane generator (only when mgmt is requested) + if ($RegenerateMgmtLibraries) { + $mgmtGeneratorPath = Join-Path $tempDir "eng" "packages" "http-client-csharp-mgmt" + if (-not (Test-Path $mgmtGeneratorPath)) { + throw "Management plane generator not found at $mgmtGeneratorPath" + } + Write-Host "##[section]Building management plane generator..." + # When publishing, query the ADO feed for the next available mgmt emitter version. + $mgmtPublishVersion = $null + if ($PublishRegistry) { + $mgmtBaseVersion = ((Get-Content (Join-Path $mgmtGeneratorPath "package.json") -Raw | ConvertFrom-Json).version -split '-')[0] + $mgmtPublishVersion = Get-NextGeneratorVersion ` + -PackageName '@azure-typespec/http-client-csharp-mgmt' ` + -BaseVersion $mgmtBaseVersion ` + -Registry $PublishRegistry ` + -NpmrcPath $PublishNpmrcPath + } + Update-MgmtGenerator ` + -EngFolder $engFolder ` + -DebugFolder $DebugFolder ` + -LocalVersion $PackageVersion ` + -PublishRegistry $PublishRegistry ` + -PublishVersion $mgmtPublishVersion ` + -AzureVersion $azurePublishVersion ` + -UnbrandedVersion $PackageVersion ` + -NpmrcPath $PublishNpmrcPath + Write-Host "Management plane generator built successfully" + } + } + + # Regenerate phase: regenerate all SDK libraries that consume the (now published) emitters. + if ($runRegenerate -and $installSucceeded) { Write-Host "Expanding sparse checkout to include sdk directory for SDK regeneration..." git sparse-checkout add sdk if ($LASTEXITCODE -ne 0) { Write-Warning "Failed to expand sparse checkout. Skipping SDK regeneration." Write-Host "##vso[task.complete result=SucceededWithIssues;]" } else { - # Build the emitter patterns to match in tsp-location.yaml + # Build the emitter patterns to match in tsp-location.yaml. The Azure/mgmt emitter + # artifacts are produced by the PublishGenerators phase and persisted on disk. $emitterPatterns = @("eng/http-client-csharp-emitter-package.json") - - if ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries) { - $regenScope = @() - if ($RegenerateAzureLibraries) { $regenScope += "Azure data plane" } - if ($RegenerateMgmtLibraries) { $regenScope += "mgmt" } - Write-Host "##[section]Building emitters locally for: $($regenScope -join ', ')..." - - # Locate the unbranded .tgz and .nupkg files from build artifacts - if (-not $BuildArtifactsPath -or -not (Test-Path $BuildArtifactsPath)) { - throw "BuildArtifactsPath is required when RegenerateAzureLibraries or RegenerateMgmtLibraries is specified. Path: $BuildArtifactsPath" - } - - $debugFolder = Join-Path ([System.IO.Path]::GetTempPath()) "csharp-debug-$(Get-Date -Format 'yyyyMMdd-HHmmss')" - New-Item -ItemType Directory -Path $debugFolder -Force | Out-Null - - # Find unbranded .tgz from build artifacts - $unbrandedTgz = Get-ChildItem -Path $BuildArtifactsPath -Filter "typespec-http-client-csharp-*.tgz" -Recurse | Select-Object -First 1 - if (-not $unbrandedTgz) { - throw "Could not find unbranded emitter .tgz in build artifacts at: $BuildArtifactsPath" - } - $unbrandedPackagePath = $unbrandedTgz.FullName - Write-Host "Using unbranded package from build artifacts: $unbrandedPackagePath" - - # Copy .nupkg files from build artifacts to debug folder - $nupkgFiles = Get-ChildItem -Path $BuildArtifactsPath -Filter "*.nupkg" -Recurse - foreach ($nupkg in $nupkgFiles) { - Copy-Item $nupkg.FullName -Destination $debugFolder -Force - Write-Host "Copied NuGet package: $($nupkg.Name)" - } - - # Build and package Azure generator (needed for both Azure data plane and mgmt) - $azureGeneratorPath = Join-Path $tempDir "eng" "packages" "http-client-csharp" - $packagesDataPropsPath = Join-Path $tempDir "eng" "centralpackagemanagement" "Directory.Generation.Packages.props" - - Write-Host "##[section]Building Azure generator..." - $previousErrorAction = $ErrorActionPreference - $ErrorActionPreference = "Continue" - # When publishing, query the ADO feed for the next available version to stamp the Azure - # emitter package with, following the existing "-alpha.." format. - $azurePublishVersion = $null - try { - if ($PublishRegistry) { - $azureBaseVersion = ((Get-Content (Join-Path $azureGeneratorPath "package.json") -Raw | ConvertFrom-Json).version -split '-')[0] - $azurePublishVersion = Get-NextGeneratorVersion ` - -PackageName '@azure-typespec/http-client-csharp' ` - -BaseVersion $azureBaseVersion ` - -Registry $PublishRegistry ` - -NpmrcPath $PublishNpmrcPath - } - $azurePackagePath = Update-AzureGenerator ` - -AzureGeneratorPath $azureGeneratorPath ` - -UnbrandedPackagePath $unbrandedPackagePath ` - -DebugFolder $debugFolder ` - -PackagesDataPropsPath $packagesDataPropsPath ` - -LocalVersion $PackageVersion ` - -PublishRegistry $PublishRegistry ` - -PublishVersion $azurePublishVersion ` - -NpmrcPath $PublishNpmrcPath - Write-Host "Azure generator built successfully" - - # Update Azure emitter package artifacts. When publishing, reference the published - # version so CI can restore it; otherwise pin to the local tgz via a "file:" path. - Write-Host "Updating Azure emitter package artifacts..." - $engFolder = Join-Path $tempDir "eng" - $azureEmitterJson = Join-Path $engFolder "azure-typespec-http-client-csharp-emitter-package.json" - $azureLockJson = Join-Path $engFolder "azure-typespec-http-client-csharp-emitter-package-lock.json" - - $updateAzureEmitterArgs = @{ - EmitterJsonPath = $azureEmitterJson - LockJsonPath = $azureLockJson - PackagePath = $azurePackagePath - } - if ($PublishRegistry) { - $updateAzureEmitterArgs.PackageName = '@azure-typespec/http-client-csharp' - $updateAzureEmitterArgs.PublishVersion = $azurePublishVersion - $updateAzureEmitterArgs.Registry = $PublishRegistry - } - Update-EmitterPackageArtifact @updateAzureEmitterArgs - - if ($RegenerateAzureLibraries) { - $emitterPatterns += "eng/azure-typespec-http-client-csharp-emitter-package.json" - } - - # Add NuGet source for local packages - $nugetConfigPath = Join-Path $tempDir "NuGet.Config" - if (Test-Path $nugetConfigPath) { - Add-LocalNuGetSource -NuGetConfigPath $nugetConfigPath -SourcePath $debugFolder - } - } catch { - Write-Warning "Failed to build Azure generator: $($_.Exception.Message). Continuing without Azure library regeneration." - Write-Host "##vso[task.complete result=SucceededWithIssues;]" - } finally { - $ErrorActionPreference = $previousErrorAction - } - - # Build and package management plane generator (only when mgmt is requested) - if ($RegenerateMgmtLibraries) { - $mgmtGeneratorPath = Join-Path $tempDir "eng" "packages" "http-client-csharp-mgmt" - if (Test-Path $mgmtGeneratorPath) { - Write-Host "##[section]Building management plane generator..." - $previousErrorAction = $ErrorActionPreference - $ErrorActionPreference = "Continue" - try { - $engFolder = Join-Path $tempDir "eng" - # When publishing, query the ADO feed for the next available mgmt emitter version. - $mgmtPublishVersion = $null - if ($PublishRegistry) { - $mgmtBaseVersion = ((Get-Content (Join-Path $mgmtGeneratorPath "package.json") -Raw | ConvertFrom-Json).version -split '-')[0] - $mgmtPublishVersion = Get-NextGeneratorVersion ` - -PackageName '@azure-typespec/http-client-csharp-mgmt' ` - -BaseVersion $mgmtBaseVersion ` - -Registry $PublishRegistry ` - -NpmrcPath $PublishNpmrcPath - } - Update-MgmtGenerator ` - -EngFolder $engFolder ` - -DebugFolder $debugFolder ` - -LocalVersion $PackageVersion ` - -PublishRegistry $PublishRegistry ` - -PublishVersion $mgmtPublishVersion ` - -AzureVersion $azurePublishVersion ` - -UnbrandedVersion $PackageVersion ` - -NpmrcPath $PublishNpmrcPath - Write-Host "Management plane generator built successfully" - - $emitterPatterns += "eng/azure-typespec-http-client-csharp-mgmt-emitter-package.json" - } catch { - Write-Warning "Failed to build management plane generator: $($_.Exception.Message). Continuing without mgmt library regeneration." - Write-Host "##vso[task.complete result=SucceededWithIssues;]" - } finally { - $ErrorActionPreference = $previousErrorAction - } - } else { - Write-Host "Management plane generator not found at $mgmtGeneratorPath, skipping..." - } - } + if ($RegenerateAzureLibraries) { + $emitterPatterns += "eng/azure-typespec-http-client-csharp-emitter-package.json" } - + if ($RegenerateMgmtLibraries) { + $emitterPatterns += "eng/azure-typespec-http-client-csharp-mgmt-emitter-package.json" + } + # Discover service directories with tsp-location.yaml referencing any of the matched emitter patterns $tspLocations = Get-ChildItem -Path (Join-Path $tempDir "sdk") -Filter "tsp-location.yaml" -Recurse $serviceDirectories = @() @@ -652,6 +707,7 @@ try { } # Regenerate the emitter version dashboard + if ($runRegenerate) { Write-Host "Regenerating emitter version dashboard..." $dashboardScript = Join-Path $tempDir "doc/GeneratorVersions/Emitter_Version_Dashboard.ps1" & $dashboardScript -RepoRoot $tempDir @@ -789,14 +845,18 @@ try { # Extract PR URL from gh output $prUrl = $ghOutput.Trim() Write-Host "Successfully created PR: $prUrl" + } # end if ($runRegenerate) } catch { Write-Error "Error creating PR: $_" exit 1 } finally { Pop-Location - # Clean up temp directory - if (Test-Path $tempDir) { + # Clean up the shared checkout only when this invocation owns its whole lifecycle. In a split + # pipeline run the Regenerate phase is the last step, so it performs the cleanup; earlier phases + # (Prepare/PublishGenerators) leave the checkout in place for the next step. + if ($Phase -in @('All', 'Regenerate') -and (Test-Path $tempDir)) { Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue + Remove-Item $prepareStateFile -Force -ErrorAction SilentlyContinue } } From d0469eccbb67496166329fb2ea5d0c63899fbd34 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:29:01 +0000 Subject: [PATCH 7/9] Reuse hoisted path variables across phases (address review) Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../eng/scripts/Submit-AzureSdkForNetPr.ps1 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 index 35005e63b96..8205abf819b 100755 --- a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 +++ b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 @@ -277,9 +277,9 @@ try { if ($runPrepare) { # Update the dependency in eng/centralpackagemanagement/Directory.Generation.Packages.props + # ($propsFilePath, $packageJsonPath and $httpClientDir are computed once above and shared across phases.) Write-Host "Updating dependency version in eng/centralpackagemanagement/Directory.Generation.Packages.props..." - $propsFilePath = Join-Path $tempDir "eng/centralpackagemanagement/Directory.Generation.Packages.props" - + if (-not (Test-Path $propsFilePath)) { throw "eng/centralpackagemanagement/Directory.Generation.Packages.props not found in the repository" } @@ -305,8 +305,7 @@ try { # Update the dependency in eng/packages/http-client-csharp/package.json Write-Host "Updating dependency version in eng/packages/http-client-csharp/package.json..." - $packageJsonPath = Join-Path $tempDir "eng/packages/http-client-csharp/package.json" - + if (-not (Test-Path $packageJsonPath)) { throw "eng/packages/http-client-csharp/package.json not found in the repository" } @@ -334,9 +333,8 @@ try { # Only run expensive operations if we actually made updates $installSucceeded = $true if ($packageJsonUpdated) { - # Run npm install in the http-client-csharp directory + # Run npm install in the http-client-csharp directory ($httpClientDir is shared, computed above) Write-Host "##[section]Running npm install in eng/packages/http-client-csharp..." - $httpClientDir = Join-Path $tempDir "eng/packages/http-client-csharp" # Update TypeSpec dependencies to @next versions in the Azure emitter package.json if ($UseTypeSpecNext) { @@ -550,9 +548,11 @@ try { Write-Host "Copied NuGet package: $($nupkg.Name)" } - # Build and package Azure generator (needed for both Azure data plane and mgmt) - $azureGeneratorPath = Join-Path $tempDir "eng" "packages" "http-client-csharp" - $packagesDataPropsPath = Join-Path $tempDir "eng" "centralpackagemanagement" "Directory.Generation.Packages.props" + # Build and package Azure generator (needed for both Azure data plane and mgmt). + # The Azure generator lives in the same eng/packages/http-client-csharp folder as $httpClientDir, + # and the generation props file is the shared $propsFilePath. + $azureGeneratorPath = $httpClientDir + $packagesDataPropsPath = $propsFilePath $engFolder = Join-Path $tempDir "eng" Write-Host "##[section]Building Azure generator..." From 9e11a2379ba43951b47bc696b7a3eb60fb5f21d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:05:17 +0000 Subject: [PATCH 8/9] Publish Azure/mgmt generators via shared publish template instead of ps1 npm publish Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../templates/stages/emitter-stages.yml | 11 +-- .../steps/publish-to-devops-feed.yml | 24 ++++++ .../eng/pipeline/publish.yml | 24 ++++-- .../eng/scripts/RegenPreview.psm1 | 74 ++----------------- .../eng/scripts/Submit-AzureSdkForNetPr.ps1 | 22 +++--- 5 files changed, 62 insertions(+), 93 deletions(-) create mode 100644 eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml diff --git a/eng/emitters/pipelines/templates/stages/emitter-stages.yml b/eng/emitters/pipelines/templates/stages/emitter-stages.yml index 336c4441685..f2d28266c1b 100644 --- a/eng/emitters/pipelines/templates/stages/emitter-stages.yml +++ b/eng/emitters/pipelines/templates/stages/emitter-stages.yml @@ -329,14 +329,9 @@ stages: - template: /eng/common/pipelines/templates/steps/create-authenticated-npmrc.yml # publish to devops feed - - pwsh: | - $packageFiles = Get-ChildItem -Path . -Filter '*.tgz' - foreach ($file in $packageFiles.Name) { - Write-Host "npm publish $file --verbose --access public" - npm publish $file --verbose --access public - } - displayName: Publish to DevOps feed - workingDirectory: $(buildArtifactsPath)/packages + - template: /eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml + parameters: + PackagesPath: $(buildArtifactsPath)/packages # If publishing publicly, also publish to npmjs.org - ${{ if eq(parameters.Publish, 'public') }}: diff --git a/eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml b/eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml new file mode 100644 index 00000000000..26f25f09e7f --- /dev/null +++ b/eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml @@ -0,0 +1,24 @@ +parameters: + # Directory containing the packed *.tgz npm packages to publish. + - name: PackagesPath + type: string + + # Condition guarding the publish step. + - name: Condition + type: string + default: succeeded() + +# Publishes all packed *.tgz npm packages found in PackagesPath to the internal +# Azure Artifacts (DevOps) feed. Callers are responsible for authenticating the +# active npm registry beforehand (for example via +# /eng/common/pipelines/templates/steps/create-authenticated-npmrc.yml). +steps: + - pwsh: | + $packageFiles = Get-ChildItem -Path . -Filter '*.tgz' + foreach ($file in $packageFiles.Name) { + Write-Host "npm publish $file --verbose --access public" + npm publish $file --verbose --access public + } + displayName: Publish to DevOps feed + workingDirectory: ${{ parameters.PackagesPath }} + condition: ${{ parameters.Condition }} diff --git a/packages/http-client-csharp/eng/pipeline/publish.yml b/packages/http-client-csharp/eng/pipeline/publish.yml index 1dfb1f4a8e5..8cd90be7630 100644 --- a/packages/http-client-csharp/eng/pipeline/publish.yml +++ b/packages/http-client-csharp/eng/pipeline/publish.yml @@ -235,14 +235,17 @@ extends: TokenOwners: - Azure - # The azure-sdk-for-net PR is produced by three discrete, independently-failing steps that + # The azure-sdk-for-net PR is produced by discrete, independently-failing steps that # share a single on-disk checkout ($(AzureSdkForNetCheckout)) and generator package folder # ($(GeneratorPackagesFolder)): # 1. Prepare - clone azure-sdk-for-net and regenerate the unbranded emitter artifacts. - # 2. Publish generators - build the Azure (and mgmt) generator from the published unbranded - # generator artifact and publish it to the ADO feed. Only runs when a - # regen was requested, and fails the pipeline on any build/publish error. - # 3. Regenerate & PR - regenerate the SDK libraries and open the azure-sdk-for-net PR. + # 2. Build generators - build and pack the Azure (and mgmt) generator from the published + # unbranded generator artifact, stamping the next available feed + # version and pinning the emitter artifacts to it. Only runs when a + # regen was requested, and fails the pipeline on any build error. + # 3. Publish generators - publish the packed generator packages to the ADO feed via the shared + # publish template (/eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml). + # 4. Regenerate & PR - regenerate the SDK libraries and open the azure-sdk-for-net PR. - task: AzureCLI@2 displayName: "Prepare azure-sdk-for-net checkout & regenerate unbranded emitter" inputs: @@ -266,7 +269,7 @@ extends: -UseTypeSpecNext:$${{ parameters.UseTypeSpecNext }} - task: AzureCLI@2 - displayName: "Build & publish Azure/mgmt generators to ADO feed" + displayName: "Build Azure/mgmt generators" condition: and(succeeded(), or(${{ parameters.RegenerateAzureLibraries }}, ${{ parameters.RegenerateMgmtLibraries }})) inputs: azureSubscription: "AzureSDKEngKeyVault Secrets" @@ -288,6 +291,15 @@ extends: -BuildArtifactsPath '$(Pipeline.Workspace)/build_artifacts_csharp/packages' -UseTypeSpecNext:$${{ parameters.UseTypeSpecNext }} + # Publish the packed Azure/mgmt generator packages to the ADO feed using the shared publish + # template. The active npm registry was already authenticated for the ADO feed by the + # create-authenticated-npmrc step above, so CI on the resulting azure-sdk-for-net PR can + # restore the emitter dependencies from the feed. + - template: /eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml + parameters: + PackagesPath: $(GeneratorPackagesFolder) + Condition: and(succeeded(), or(${{ parameters.RegenerateAzureLibraries }}, ${{ parameters.RegenerateMgmtLibraries }})) + - task: AzureCLI@2 displayName: "Regenerate SDK libraries & create PR in azure-sdk-for-net" inputs: diff --git a/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 b/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 index 3c5c3d01050..ba13a34e447 100644 --- a/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 +++ b/packages/http-client-csharp/eng/scripts/RegenPreview.psm1 @@ -4,69 +4,6 @@ Import-Module "$PSScriptRoot\Generation.psm1" -DisableNameChecking -Force -Globa # This is the public azure-sdk-for-js Azure Artifacts feed. $script:DefaultGeneratorRegistry = "https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-js/npm/registry/" -function Publish-GeneratorPackage { - <# - .SYNOPSIS - Publishes a locally built generator package (.tgz) to an npm registry. - - .DESCRIPTION - Publishing the locally built generator packages to the configured registry allows the - regenerated emitter-package.json artifacts to reference a published version instead of a - host-only "file:" path. That way the resulting azure-sdk-for-net PR can restore the emitter - dependencies in CI. - - Re-publishing a version that already exists on the feed is treated as success so the operation - is idempotent across pipeline re-runs. - - .PARAMETER PackagePath - Path to the packed .tgz file to publish. - - .PARAMETER Registry - The npm registry to publish to. - - .PARAMETER NpmrcPath - Optional path to an .npmrc file (with authentication) to use for the publish operation. - #> - param( - [Parameter(Mandatory=$true)] - [string]$PackagePath, - - [Parameter(Mandatory=$true)] - [string]$Registry, - - [Parameter(Mandatory=$false)] - [string]$NpmrcPath - ) - - if (-not (Test-Path $PackagePath)) { - throw "Package not found for publishing: $PackagePath" - } - - Write-Host "Publishing $(Split-Path $PackagePath -Leaf) to $Registry..." -ForegroundColor Gray - - $publishArgs = @("publish", $PackagePath, "--registry", $Registry) - if ($NpmrcPath -and (Test-Path $NpmrcPath)) { - $publishArgs += @("--userconfig", $NpmrcPath) - } - - $publishOutput = & npm @publishArgs 2>&1 - if ($LASTEXITCODE -ne 0) { - $outputText = ($publishOutput | Out-String) - # Treat an already-published version as success so re-runs remain idempotent. - if ($outputText -match "EPUBLISHCONFLICT" -or - $outputText -match "cannot publish over" -or - $outputText -match "previously published" -or - $outputText -match "409 Conflict") { - Write-Host " Package version already published; continuing." -ForegroundColor Yellow - return - } - Write-Host $outputText -ForegroundColor Red - throw "Failed to publish package: $PackagePath" - } - - Write-Host " Published $(Split-Path $PackagePath -Leaf)" -ForegroundColor Green -} - function Get-NextGeneratorVersion { <# .SYNOPSIS @@ -451,11 +388,10 @@ function Update-GeneratorPackage { Write-Host " Package created: $packageFile" -ForegroundColor Green - # Step 4 (optional): Publish the package so downstream emitter artifacts can reference a - # restorable published version instead of a host-only "file:" path. - if ($PublishRegistry) { - Publish-GeneratorPackage -PackagePath $destPath -Registry $PublishRegistry -NpmrcPath $NpmrcPath - } + # NOTE: publishing the packed .tgz to the feed is handled by the pipeline via the shared + # publish template (/eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml), not + # here. This function only stamps the next available feed version and packs the .tgz so the + # downstream emitter artifacts can reference a restorable published version. return $destPath } @@ -1339,4 +1275,4 @@ function Update-AzureSpectorScenarios { return $generationOutput } -Export-ModuleMember -Function "Update-MgmtGenerator", "Update-AzureGenerator", "Filter-LibrariesByGenerator", "Update-OpenAIGenerator", "Add-LocalNuGetSource", "Update-AzureSpectorScenarios", "Publish-GeneratorPackage", "Update-EmitterPackageArtifact", "Get-NextGeneratorVersion" +Export-ModuleMember -Function "Update-MgmtGenerator", "Update-AzureGenerator", "Filter-LibrariesByGenerator", "Update-OpenAIGenerator", "Add-LocalNuGetSource", "Update-AzureSpectorScenarios", "Update-EmitterPackageArtifact", "Get-NextGeneratorVersion" diff --git a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 index 8205abf819b..4a9a63530aa 100755 --- a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 +++ b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 @@ -24,7 +24,7 @@ The URL of the pipeline run that triggered this PR. When provided, it is include .PARAMETER Phase Which phase of the flow to run. The publish pipeline drives these as discrete, independently-failing steps that share a single on-disk checkout: - Prepare: Clone azure-sdk-for-net, update the generator version, regenerate the unbranded test projects and emitter-package.json artifacts. - - PublishGenerators: Build the Azure (and mgmt) generator from the published unbranded generator artifact and publish it to the ADO feed, pinning the emitter artifacts to the published version. + - PublishGenerators: Build and pack the Azure (and mgmt) generator from the published unbranded generator artifact, stamping the next available feed version and pinning the emitter artifacts to it. The packed packages are published to the ADO feed by the pipeline via the shared publish template (/eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml). - Regenerate: Regenerate the SDK libraries and open the azure-sdk-for-net PR. Defaults to 'All', which runs every phase in a single invocation (used for local, non-pipeline runs). .PARAMETER WorkingDirectory @@ -83,10 +83,11 @@ Import-Module (Join-Path $PSScriptRoot "Generation.psm1") -DisableNameChecking - # Import RegenPreview module for Update-AzureGenerator and Update-MgmtGenerator Import-Module (Join-Path $PSScriptRoot "RegenPreview.psm1") -DisableNameChecking -Force -# Publishing the locally built generator packages happens by default whenever a regeneration is -# requested, so the regenerated emitter-package.json artifacts reference a published version instead -# of a host-only "file:" path. That way CI can restore the emitter dependencies in the resulting -# azure-sdk-for-net PR. Resolve the registry and authenticated .npmrc used for publishing. +# The locally built Azure/mgmt generator packages are published to the ADO feed by the pipeline via +# the shared publish template (see publish.yml). This script only stamps the next available feed +# version onto the packed packages and pins the regenerated emitter-package.json artifacts to it, so +# CI can restore the emitter dependencies in the resulting azure-sdk-for-net PR. Resolve the registry +# and authenticated .npmrc used to query the feed for the next available version. $PublishRegistry = $null $PublishNpmrcPath = $null if ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries) { @@ -516,15 +517,16 @@ try { @{ InstallSucceeded = $installSucceeded } | ConvertTo-Json | Set-Content $prepareStateFile -Encoding utf8 } # end if ($runPrepare) - # PublishGenerators phase: build the Azure (and mgmt) generator from the published unbranded - # generator artifact and publish it to the ADO feed, pinning the emitter artifacts to the published - # version. This runs as a discrete, fail-hard pipeline step (see publish.yml) so any build/publish - # failure fails the pipeline instead of silently producing an unrestorable PR. + # PublishGenerators phase: build and pack the Azure (and mgmt) generator from the published + # unbranded generator artifact, stamping the next available feed version and pinning the emitter + # artifacts to it. This runs as a discrete, fail-hard pipeline step (see publish.yml) so any build + # failure fails the pipeline instead of silently producing an unrestorable PR. The packed packages + # are published to the ADO feed by the pipeline via the shared publish template. if ($runPublishGenerators -and $installSucceeded -and ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries)) { $regenScope = @() if ($RegenerateAzureLibraries) { $regenScope += "Azure data plane" } if ($RegenerateMgmtLibraries) { $regenScope += "mgmt" } - Write-Host "##[section]Building and publishing generators for: $($regenScope -join ', ')..." + Write-Host "##[section]Building generators for: $($regenScope -join ', ')..." # Locate the unbranded .tgz and .nupkg files from build artifacts if (-not $BuildArtifactsPath -or -not (Test-Path $BuildArtifactsPath)) { From d241888bb6b440405abc0411b25849570517b597 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 18:06:24 +0000 Subject: [PATCH 9/9] Rename PublishGenerators phase to BuildGenerators to match its build-only scope Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../eng/pipeline/publish.yml | 2 +- .../eng/scripts/Submit-AzureSdkForNetPr.ps1 | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/http-client-csharp/eng/pipeline/publish.yml b/packages/http-client-csharp/eng/pipeline/publish.yml index 8cd90be7630..314258ad7e7 100644 --- a/packages/http-client-csharp/eng/pipeline/publish.yml +++ b/packages/http-client-csharp/eng/pipeline/publish.yml @@ -277,7 +277,7 @@ extends: scriptLocation: scriptPath scriptPath: $(Build.SourcesDirectory)/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 arguments: > - -Phase 'PublishGenerators' + -Phase 'BuildGenerators' -WorkingDirectory '$(AzureSdkForNetCheckout)' -DebugFolder '$(GeneratorPackagesFolder)' -PackageVersion '$(PackageVersion)' diff --git a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 index 4a9a63530aa..1a3f6bc94d6 100755 --- a/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 +++ b/packages/http-client-csharp/eng/scripts/Submit-AzureSdkForNetPr.ps1 @@ -24,13 +24,13 @@ The URL of the pipeline run that triggered this PR. When provided, it is include .PARAMETER Phase Which phase of the flow to run. The publish pipeline drives these as discrete, independently-failing steps that share a single on-disk checkout: - Prepare: Clone azure-sdk-for-net, update the generator version, regenerate the unbranded test projects and emitter-package.json artifacts. - - PublishGenerators: Build and pack the Azure (and mgmt) generator from the published unbranded generator artifact, stamping the next available feed version and pinning the emitter artifacts to it. The packed packages are published to the ADO feed by the pipeline via the shared publish template (/eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml). + - BuildGenerators: Build and pack the Azure (and mgmt) generator from the published unbranded generator artifact, stamping the next available feed version and pinning the emitter artifacts to it. The packed packages are published to the ADO feed by the pipeline via the shared publish template (/eng/emitters/pipelines/templates/steps/publish-to-devops-feed.yml). - Regenerate: Regenerate the SDK libraries and open the azure-sdk-for-net PR. Defaults to 'All', which runs every phase in a single invocation (used for local, non-pipeline runs). .PARAMETER WorkingDirectory The azure-sdk-for-net checkout directory shared across phases. When omitted (typically for the 'All' phase), a unique temp directory is created and cleaned up automatically. .PARAMETER DebugFolder -The directory holding the packed generator packages and NuGet packages shared between the PublishGenerators and Regenerate phases. When omitted, a unique temp directory is derived from WorkingDirectory. +The directory holding the packed generator packages and NuGet packages shared between the BuildGenerators and Regenerate phases. When omitted, a unique temp directory is derived from WorkingDirectory. #> [CmdletBinding(SupportsShouldProcess = $true)] param( @@ -68,7 +68,7 @@ param( [switch]$UseTypeSpecNext, [Parameter(Mandatory = $false)] - [ValidateSet('All', 'Prepare', 'PublishGenerators', 'Regenerate')] + [ValidateSet('All', 'Prepare', 'BuildGenerators', 'Regenerate')] [string]$Phase = 'All', [Parameter(Mandatory = $false)] @@ -104,7 +104,7 @@ if ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries) { # The publish pipeline drives this script as three discrete, independently-failing steps that share a # single on-disk azure-sdk-for-net checkout (see publish.yml). Resolve which phases this invocation runs. $runPrepare = $Phase -in @('All', 'Prepare') -$runPublishGenerators = $Phase -in @('All', 'PublishGenerators') +$runBuildGenerators = $Phase -in @('All', 'BuildGenerators') $runRegenerate = $Phase -in @('All', 'Regenerate') # Set up variables for the PR @@ -171,7 +171,7 @@ Write-Host "Branch: $PRBranch" Write-Host "Title: $PRTitle" # Resolve the shared checkout directory. The publish pipeline passes an explicit, deterministic -# -WorkingDirectory so the Prepare / PublishGenerators / Regenerate steps operate on the same on-disk +# -WorkingDirectory so the Prepare / BuildGenerators / Regenerate steps operate on the same on-disk # checkout. For a single 'All' invocation (e.g. local runs) fall back to a unique temp directory. if ($WorkingDirectory) { $tempDir = $WorkingDirectory @@ -179,7 +179,7 @@ if ($WorkingDirectory) { $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "azure-sdk-for-net-$(Get-Date -Format 'yyyyMMdd-HHmmss')" } -# The debug folder holds the packed generator + NuGet packages shared between the PublishGenerators and +# The debug folder holds the packed generator + NuGet packages shared between the BuildGenerators and # Regenerate phases. Derive a deterministic default from the checkout so both phases agree. if (-not $DebugFolder) { $DebugFolder = Join-Path $tempDir ".generator-packages" @@ -512,17 +512,17 @@ try { Write-Warning "TypeSpecSourcePackageJsonPath not provided or file doesn't exist. Skipping emitter-package.json generation." } - # Persist the resolved install state so the later PublishGenerators / Regenerate phases (which may + # Persist the resolved install state so the later BuildGenerators / Regenerate phases (which may # run as separate processes) honor the same gating as a single 'All' invocation. @{ InstallSucceeded = $installSucceeded } | ConvertTo-Json | Set-Content $prepareStateFile -Encoding utf8 } # end if ($runPrepare) - # PublishGenerators phase: build and pack the Azure (and mgmt) generator from the published + # BuildGenerators phase: build and pack the Azure (and mgmt) generator from the published # unbranded generator artifact, stamping the next available feed version and pinning the emitter # artifacts to it. This runs as a discrete, fail-hard pipeline step (see publish.yml) so any build # failure fails the pipeline instead of silently producing an unrestorable PR. The packed packages # are published to the ADO feed by the pipeline via the shared publish template. - if ($runPublishGenerators -and $installSucceeded -and ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries)) { + if ($runBuildGenerators -and $installSucceeded -and ($RegenerateAzureLibraries -or $RegenerateMgmtLibraries)) { $regenScope = @() if ($RegenerateAzureLibraries) { $regenScope += "Azure data plane" } if ($RegenerateMgmtLibraries) { $regenScope += "mgmt" } @@ -643,7 +643,7 @@ try { Write-Host "##vso[task.complete result=SucceededWithIssues;]" } else { # Build the emitter patterns to match in tsp-location.yaml. The Azure/mgmt emitter - # artifacts are produced by the PublishGenerators phase and persisted on disk. + # artifacts are produced by the BuildGenerators phase and persisted on disk. $emitterPatterns = @("eng/http-client-csharp-emitter-package.json") if ($RegenerateAzureLibraries) { $emitterPatterns += "eng/azure-typespec-http-client-csharp-emitter-package.json" @@ -856,7 +856,7 @@ try { Pop-Location # Clean up the shared checkout only when this invocation owns its whole lifecycle. In a split # pipeline run the Regenerate phase is the last step, so it performs the cleanup; earlier phases - # (Prepare/PublishGenerators) leave the checkout in place for the next step. + # (Prepare/BuildGenerators) leave the checkout in place for the next step. if ($Phase -in @('All', 'Regenerate') -and (Test-Path $tempDir)) { Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue Remove-Item $prepareStateFile -Force -ErrorAction SilentlyContinue