Skip to content

Commit 49dfdcc

Browse files
baywetCopilotCopilot
authored
ci(release): port ESRP NuGet publishing to support/v2 (#3110)
* ci(release): publish OpenAPI packages and symbols through ESRP Co-authored-by: baywet <7905502+baywet@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ec81052a-ebe3-41e4-877c-46fb21141af7 * ci(release): share authenticated private-feed version checks Co-authored-by: baywet <7905502+baywet@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ec81052a-ebe3-41e4-877c-46fb21141af7 * ci(release): align NuGet publication checks across repositories Port commit 8ed2fbc from #3107. Preserve the shared authenticated publication script and update both remaining library deployment call sites; omit Hidi and Docker changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ec81052a-ebe3-41e4-877c-46fb21141af7 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: baywet <7905502+baywet@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ec81052a-ebe3-41e4-877c-46fb21141af7
1 parent c8e64dc commit 49dfdcc

2 files changed

Lines changed: 176 additions & 14 deletions

File tree

‎.azure-pipelines/ci-build.yml‎

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pr:
2121
variables:
2222
buildPlatform: 'Any CPU'
2323
buildConfiguration: 'Release'
24+
NuGetOrganizationName: 'openapinet'
25+
privateFeedBaseUrl: 'https://microsoftgraph.pkgs.visualstudio.com/0985d294-5762-4bc2-a565-161ef349ca3e/_packaging/GraphDeveloperExperiences_Public'
2426
ProductBinPath: '$(Build.SourcesDirectory)\src\Microsoft.OpenApi\bin\$(BuildConfiguration)'
2527

2628
resources:
@@ -81,7 +83,7 @@ extends:
8183
<configuration>
8284
<packageSources>
8385
<clear />
84-
<add key="GraphDeveloperExperiences_Public" value="https://microsoftgraph.pkgs.visualstudio.com/0985d294-5762-4bc2-a565-161ef349ca3e/_packaging/GraphDeveloperExperiences_Public/nuget/v3/index.json" />
86+
<add key="GraphDeveloperExperiences_Public" value="$(privateFeedBaseUrl)/nuget/v3/index.json" />
8587
</packageSources>
8688
</configuration>
8789
"@ | Set-Content -Path "$(Build.SourcesDirectory)/nuget.config" -Encoding UTF8
@@ -205,7 +207,16 @@ extends:
205207
inputs:
206208
targetFolder: $(Build.ArtifactStagingDirectory)/Nugets
207209
sourceFolder: $(Build.ArtifactStagingDirectory)
208-
content: '*.nupkg'
210+
Contents: |
211+
*.nupkg
212+
*.snupkg
213+
214+
- task: CopyFiles@2
215+
displayName: 'Include version-check script in Nugets artifact'
216+
inputs:
217+
SourceFolder: '$(Build.SourcesDirectory)/scripts'
218+
Contents: 'check-nuget-package-published.ps1'
219+
TargetFolder: '$(Build.ArtifactStagingDirectory)/Nugets/scripts'
209220

210221
- stage: deploy
211222
condition: and(contains(variables['Build.SourceBranch'], 'refs/tags/v'), succeeded())
@@ -232,13 +243,47 @@ extends:
232243
pool:
233244
vmImage: ubuntu-latest
234245
steps:
235-
- task: 1ES.PublishNuget@1
236-
displayName: 'NuGet push'
246+
- task: PowerShell@2
247+
displayName: 'Check whether NuGet package version already published (idempotent)'
248+
inputs:
249+
targetType: filePath
250+
filePath: '$(Pipeline.Workspace)/scripts/check-nuget-package-published.ps1'
251+
arguments: '-PackageId "Microsoft.OpenApi" -PackageDirectory "$(Pipeline.Workspace)" -NuGetServiceIndexUrl "$(privateFeedBaseUrl)/nuget/v3/index.json"'
252+
pwsh: true
253+
env:
254+
FEED_ACCESS_TOKEN: $(System.AccessToken)
255+
- task: CopyFiles@2
256+
displayName: 'Stage OpenAPI NuGet packages for ESRP release'
257+
condition: and(succeeded(), ne(variables['nugetAlreadyPublished'], 'true'))
237258
inputs:
238-
packagesToPush: '$(Pipeline.Workspace)/Microsoft.OpenApi.[0-9]*.nupkg'
239-
packageParentPath: '$(Pipeline.Workspace)'
240-
nuGetFeedType: external
241-
publishFeedCredentials: 'OpenAPI Nuget Connection'
259+
SourceFolder: '$(Pipeline.Workspace)'
260+
Contents: |
261+
Microsoft.OpenApi.*.nupkg
262+
Microsoft.OpenApi.*.snupkg
263+
!Microsoft.OpenApi.YamlReader.*
264+
TargetFolder: '$(Pipeline.Workspace)/nuget-packages/$(NuGetOrganizationName)/openapi'
265+
CleanTargetFolder: true
266+
- task: EsrpRelease@14
267+
displayName: 'ESRP Release - OpenAPI NuGet'
268+
condition: and(succeeded(), ne(variables['nugetAlreadyPublished'], 'true'))
269+
inputs:
270+
connectedservicename: 'Federated DevX ESRP Managed Identity Connection'
271+
usemanagedidentity: false
272+
keyvaultname: 'akv-prod-eastus'
273+
authcertname: 'ReferenceLibraryPrivateCert'
274+
signcertname: 'ReferencePackagePublisherCertificate'
275+
clientid: '65035b7f-7357-4f29-bf25-c5ee5c3949f8'
276+
intent: 'packagedistribution'
277+
contenttype: 'NuGet'
278+
organizationname: '$(NuGetOrganizationName)'
279+
contentsource: 'Folder'
280+
folderlocation: '$(Pipeline.Workspace)/nuget-packages/$(NuGetOrganizationName)/openapi'
281+
waitforreleasecompletion: true
282+
owners: 'christiano@microsoft.com,ramsess@microsoft.com,gavinbarron@microsoft.com,jingjingjia@microsoft.com,peombwa@microsoft.com,treicys@microsoft.com'
283+
approvers: 'christiano@microsoft.com,ramsess@microsoft.com,gavinbarron@microsoft.com,jingjingjia@microsoft.com,peombwa@microsoft.com,treicys@microsoft.com'
284+
serviceendpointurl: 'https://api.esrp.microsoft.com/'
285+
mainpublisher: 'ESRPRELPACMAN'
286+
domaintenantid: 'cdc5aeea-15c5-4db6-b079-fcadd2505dc2'
242287

243288
- deployment: deploy_yaml_reader
244289
condition: and(contains(variables['build.SourceBranch'], 'refs/tags/v'), succeeded())
@@ -257,13 +302,46 @@ extends:
257302
pool:
258303
vmImage: ubuntu-latest
259304
steps:
260-
- task: 1ES.PublishNuget@1
261-
displayName: 'NuGet push'
305+
- task: PowerShell@2
306+
displayName: 'Check whether NuGet package version already published (idempotent)'
307+
inputs:
308+
targetType: filePath
309+
filePath: '$(Pipeline.Workspace)/scripts/check-nuget-package-published.ps1'
310+
arguments: '-PackageId "Microsoft.OpenApi.YamlReader" -PackageDirectory "$(Pipeline.Workspace)" -NuGetServiceIndexUrl "$(privateFeedBaseUrl)/nuget/v3/index.json"'
311+
pwsh: true
312+
env:
313+
FEED_ACCESS_TOKEN: $(System.AccessToken)
314+
- task: CopyFiles@2
315+
displayName: 'Stage YAML reader NuGet packages for ESRP release'
316+
condition: and(succeeded(), ne(variables['nugetAlreadyPublished'], 'true'))
317+
inputs:
318+
SourceFolder: '$(Pipeline.Workspace)'
319+
Contents: |
320+
Microsoft.OpenApi.YamlReader.*.nupkg
321+
Microsoft.OpenApi.YamlReader.*.snupkg
322+
TargetFolder: '$(Pipeline.Workspace)/nuget-packages/$(NuGetOrganizationName)/yaml-reader'
323+
CleanTargetFolder: true
324+
- task: EsrpRelease@14
325+
displayName: 'ESRP Release - YAML reader NuGet'
326+
condition: and(succeeded(), ne(variables['nugetAlreadyPublished'], 'true'))
262327
inputs:
263-
packagesToPush: '$(Pipeline.Workspace)/Microsoft.OpenApi.YamlReader.*.nupkg'
264-
packageParentPath: '$(Pipeline.Workspace)'
265-
nuGetFeedType: external
266-
publishFeedCredentials: 'OpenAPI Nuget Connection'
328+
connectedservicename: 'Federated DevX ESRP Managed Identity Connection'
329+
usemanagedidentity: false
330+
keyvaultname: 'akv-prod-eastus'
331+
authcertname: 'ReferenceLibraryPrivateCert'
332+
signcertname: 'ReferencePackagePublisherCertificate'
333+
clientid: '65035b7f-7357-4f29-bf25-c5ee5c3949f8'
334+
intent: 'packagedistribution'
335+
contenttype: 'NuGet'
336+
organizationname: '$(NuGetOrganizationName)'
337+
contentsource: 'Folder'
338+
folderlocation: '$(Pipeline.Workspace)/nuget-packages/$(NuGetOrganizationName)/yaml-reader'
339+
waitforreleasecompletion: true
340+
owners: 'christiano@microsoft.com,ramsess@microsoft.com,gavinbarron@microsoft.com,jingjingjia@microsoft.com,peombwa@microsoft.com,treicys@microsoft.com'
341+
approvers: 'christiano@microsoft.com,ramsess@microsoft.com,gavinbarron@microsoft.com,jingjingjia@microsoft.com,peombwa@microsoft.com,treicys@microsoft.com'
342+
serviceendpointurl: 'https://api.esrp.microsoft.com/'
343+
mainpublisher: 'ESRPRELPACMAN'
344+
domaintenantid: 'cdc5aeea-15c5-4db6-b079-fcadd2505dc2'
267345

268346
- deployment: create_github_release
269347
condition: and(contains(variables['build.SourceBranch'], 'refs/tags/v'), succeeded())
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
<#
5+
.SYNOPSIS
6+
Checks whether a NuGet artifact's version exists in an authenticated Azure Artifacts feed.
7+
.DESCRIPTION
8+
Resolves the package content endpoint from the private feed's NuGet v3 service index.
9+
Sets nugetAlreadyPublished for the ESRP release steps; only a missing package or version
10+
permits publishing. Feed authentication and other lookup failures fail the step.
11+
#>
12+
[CmdletBinding()]
13+
param(
14+
[Parameter(Mandatory = $true)]
15+
[string]$PackageDirectory,
16+
[Parameter(Mandatory = $true)]
17+
[string]$PackageId,
18+
[Parameter(Mandatory = $true)]
19+
[string]$NuGetServiceIndexUrl,
20+
[string]$FeedAccessToken = $env:FEED_ACCESS_TOKEN
21+
)
22+
23+
$ErrorActionPreference = 'Stop'
24+
25+
function Assert-PrivateFeedUrl {
26+
param([string]$Url)
27+
28+
$uri = [uri]$Url
29+
if (-not $uri.IsAbsoluteUri -or $uri.Scheme -ne 'https' -or
30+
($uri.Host -ne 'pkgs.dev.azure.com' -and -not $uri.Host.EndsWith('.pkgs.visualstudio.com'))) {
31+
throw "NuGet lookups must use an HTTPS Azure Artifacts feed: $Url"
32+
}
33+
}
34+
35+
Assert-PrivateFeedUrl -Url $NuGetServiceIndexUrl
36+
if ([string]::IsNullOrWhiteSpace($FeedAccessToken)) {
37+
throw 'FEED_ACCESS_TOKEN is required to query the private NuGet feed.'
38+
}
39+
40+
$packagePattern = '^' + [regex]::Escape($PackageId) + '\.(\d[\w\.\-]*)\.nupkg$'
41+
$packages = @(Get-ChildItem -Path $PackageDirectory -File -Filter "$PackageId.*.nupkg" |
42+
Where-Object { $_.Name -match $packagePattern })
43+
if ($packages.Count -ne 1) {
44+
throw "Expected exactly one $PackageId nupkg to publish; found $($packages.Count)."
45+
}
46+
$version = [regex]::Match($packages[0].Name, $packagePattern, 'IgnoreCase').Groups[1].Value
47+
$id = $PackageId.ToLowerInvariant()
48+
$credentials = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("AzureDevOps:$FeedAccessToken"))
49+
$headers = @{
50+
'Authorization' = "Basic $credentials"
51+
'User-Agent' = 'openapi-azdo-pipeline'
52+
}
53+
54+
$index = Invoke-RestMethod -Uri $NuGetServiceIndexUrl -Headers $headers -MaximumRedirection 0
55+
$resource = $index.resources | Where-Object { $_.'@type' -eq 'PackageBaseAddress/3.0.0' } | Select-Object -First 1
56+
if ([string]::IsNullOrWhiteSpace($resource.'@id')) {
57+
throw "No PackageBaseAddress resource found in the NuGet service index at $NuGetServiceIndexUrl"
58+
}
59+
$uri = "$($resource.'@id'.TrimEnd('/'))/$id/index.json"
60+
Assert-PrivateFeedUrl -Url $uri
61+
62+
try {
63+
$response = Invoke-RestMethod -Uri $uri -Headers $headers -MaximumRedirection 0
64+
if ($null -eq $response.versions) {
65+
throw "No versions returned for NuGet $id by the private feed."
66+
}
67+
$alreadyPublished = $response.versions -contains $version
68+
}
69+
catch {
70+
if ([int]$_.Exception.Response.StatusCode -eq 404) {
71+
$alreadyPublished = $false
72+
}
73+
else {
74+
throw
75+
}
76+
}
77+
78+
if ($alreadyPublished) {
79+
Write-Host "NuGet $id $version already present in the private feed; skipping ESRP release (idempotent re-run)."
80+
}
81+
else {
82+
Write-Host "NuGet $id $version not found in the private feed; will publish via ESRP."
83+
}
84+
Write-Host "##vso[task.setvariable variable=nugetAlreadyPublished]$($alreadyPublished.ToString().ToLowerInvariant())"

0 commit comments

Comments
 (0)