-
Notifications
You must be signed in to change notification settings - Fork 855
Move VS Code extension Marketplace publishing to release-publish-nuget pipeline #14847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| # Release Pipeline: Publish NuGet Packages and Promote to GA Channel | ||||||||||||||||||||||||||||||||||||||||||||
| # Release Pipeline: Publish NuGet Packages, VS Code Extension, and Promote to GA Channel | ||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||
| # This pipeline automates the release process for dotnet/aspire: | ||||||||||||||||||||||||||||||||||||||||||||
| # 1. Downloads signed packages from a specified build | ||||||||||||||||||||||||||||||||||||||||||||
| # 2. Publishes packages to NuGet.org | ||||||||||||||||||||||||||||||||||||||||||||
| # 3. Promotes the build to the Aspire GA channel via darc | ||||||||||||||||||||||||||||||||||||||||||||
| # 4. Submits WinGet manifests and Homebrew cask PRs | ||||||||||||||||||||||||||||||||||||||||||||
| # 3. Publishes VS Code extension to the Marketplace | ||||||||||||||||||||||||||||||||||||||||||||
| # 4. Promotes the build to the Aspire GA channel via darc | ||||||||||||||||||||||||||||||||||||||||||||
| # 5. Submits WinGet manifests and Homebrew cask PRs | ||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||
| # For full documentation, see: docs/release-process.md | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -43,10 +44,26 @@ parameters: | |||||||||||||||||||||||||||||||||||||||||||
| type: boolean | ||||||||||||||||||||||||||||||||||||||||||||
| default: false | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # VS Code Extension Publishing Parameters | ||||||||||||||||||||||||||||||||||||||||||||
| - name: PublishVSCodeExtension | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'Publish VS Code Extension to Marketplace' | ||||||||||||||||||||||||||||||||||||||||||||
| type: boolean | ||||||||||||||||||||||||||||||||||||||||||||
| default: false | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - name: VSCodeExtensionPreRelease | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'Publish VS Code Extension as Pre-Release' | ||||||||||||||||||||||||||||||||||||||||||||
| type: boolean | ||||||||||||||||||||||||||||||||||||||||||||
| default: false | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - name: SkipExtensionPublish | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'Skip VS Code Extension Publishing (set true if already completed)' | ||||||||||||||||||||||||||||||||||||||||||||
| type: boolean | ||||||||||||||||||||||||||||||||||||||||||||
| default: false | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| variables: | ||||||||||||||||||||||||||||||||||||||||||||
| - template: /eng/pipelines/common-variables.yml@self | ||||||||||||||||||||||||||||||||||||||||||||
| - template: /eng/common/templates-official/variables/pool-providers.yml@self | ||||||||||||||||||||||||||||||||||||||||||||
| # Variable group containing secrets (VscePublishToken for future use) | ||||||||||||||||||||||||||||||||||||||||||||
| # Variable group containing secrets (VscePublishToken, etc.) | ||||||||||||||||||||||||||||||||||||||||||||
| # Note: NuGet publishing uses service connection 'NuGet.org - dotnet/aspire' instead of API key | ||||||||||||||||||||||||||||||||||||||||||||
| - group: Aspire-Release-Secrets | ||||||||||||||||||||||||||||||||||||||||||||
| # Variable group containing aspire-winget-bot-pat and aspire-homebrew-bot-pat for WinGet and Homebrew publishing | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -225,6 +242,10 @@ extends: | |||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Dry Run: ${{ parameters.DryRun }}" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Skip NuGet Publish: ${{ parameters.SkipNuGetPublish }}" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Skip Channel Promotion: ${{ parameters.SkipChannelPromotion }}" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "--- VS Code Extension ---" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Publish VS Code Extension: ${{ parameters.PublishVSCodeExtension }}" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Extension Pre-Release: ${{ parameters.VSCodeExtensionPreRelease }}" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Skip Extension Publish: ${{ parameters.SkipExtensionPublish }}" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "===================================" | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'Validate Parameters' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -454,17 +475,26 @@ extends: | |||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ GA Channel: ${{ parameters.GaChannelName }}" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ Dry Run: ${{ parameters.DryRun }}" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "╠═══════════════════════════════════════════════════════════════╣" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ NuGet Publish: ${{ parameters.SkipNuGetPublish }}" -NoNewline | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ NuGet Publish: " -NoNewline | ||||||||||||||||||||||||||||||||||||||||||||
| if ("${{ parameters.SkipNuGetPublish }}" -eq "true") { | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host " (SKIPPED)" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "SKIPPED" | ||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host " (EXECUTED)" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "EXECUTED" | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ Channel Promo: ${{ parameters.SkipChannelPromotion }}" -NoNewline | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ VS Code Ext: " -NoNewline | ||||||||||||||||||||||||||||||||||||||||||||
| if ("${{ parameters.PublishVSCodeExtension }}" -eq "false") { | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "NOT REQUESTED" | ||||||||||||||||||||||||||||||||||||||||||||
| } elseif ("${{ parameters.SkipExtensionPublish }}" -eq "true") { | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "SKIPPED" | ||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||
| $preRelease = if ("${{ parameters.VSCodeExtensionPreRelease }}" -eq "true") { " (pre-release)" } else { "" } | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "EXECUTED$preRelease" | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+478
to
+492
|
||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ Channel Promo: " -NoNewline | ||||||||||||||||||||||||||||||||||||||||||||
| if ("${{ parameters.SkipChannelPromotion }}" -eq "true") { | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host " (SKIPPED)" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "SKIPPED" | ||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host " (EXECUTED)" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "EXECUTED" | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ WinGet: (runs in parallel after this job)" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "║ Homebrew: (runs in parallel after this job)" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -528,3 +558,148 @@ extends: | |||||||||||||||||||||||||||||||||||||||||||
| caskArtifactPath: $(Pipeline.Workspace)/homebrew/homebrew-cask-stable | ||||||||||||||||||||||||||||||||||||||||||||
| channel: stable | ||||||||||||||||||||||||||||||||||||||||||||
| dryRun: ${{ parameters.DryRun }} | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # ===== VS CODE EXTENSION PUBLISHING ===== | ||||||||||||||||||||||||||||||||||||||||||||
| - job: VSCodeExtensionJob | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'Publish VS Code Extension to Marketplace' | ||||||||||||||||||||||||||||||||||||||||||||
| dependsOn: ReleaseJob | ||||||||||||||||||||||||||||||||||||||||||||
| condition: | | ||||||||||||||||||||||||||||||||||||||||||||
| and( | ||||||||||||||||||||||||||||||||||||||||||||
| in(dependencies.ReleaseJob.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'), | ||||||||||||||||||||||||||||||||||||||||||||
| eq('${{ parameters.PublishVSCodeExtension }}', 'true'), | ||||||||||||||||||||||||||||||||||||||||||||
| eq('${{ parameters.SkipExtensionPublish }}', 'false') | ||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||
| timeoutInMinutes: 30 | ||||||||||||||||||||||||||||||||||||||||||||
| pool: | ||||||||||||||||||||||||||||||||||||||||||||
| name: NetCore1ESPool-Internal | ||||||||||||||||||||||||||||||||||||||||||||
| image: windows.vs2026preview.scout.amd64 | ||||||||||||||||||||||||||||||||||||||||||||
| os: windows | ||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||
| - checkout: none | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - task: NodeTool@0 | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'Install Node.js' | ||||||||||||||||||||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||||||||||||||||||||
| versionSpec: '20.x' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - powershell: | | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Installing vsce CLI..." | ||||||||||||||||||||||||||||||||||||||||||||
| npm install -g @vscode/vsce@3.7.1 | ||||||||||||||||||||||||||||||||||||||||||||
| vsce --version | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'Install vsce' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - task: DownloadBuildArtifacts@0 | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'Download VS Code Extension Artifact' | ||||||||||||||||||||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||||||||||||||||||||
| buildType: specific | ||||||||||||||||||||||||||||||||||||||||||||
| buildVersionToDownload: specific | ||||||||||||||||||||||||||||||||||||||||||||
| project: internal | ||||||||||||||||||||||||||||||||||||||||||||
| pipeline: dotnet-aspire | ||||||||||||||||||||||||||||||||||||||||||||
| buildId: $(resources.pipeline.aspire-build.runID) | ||||||||||||||||||||||||||||||||||||||||||||
| artifactName: aspire-vscode-extension | ||||||||||||||||||||||||||||||||||||||||||||
| downloadPath: '$(Pipeline.Workspace)/extension' | ||||||||||||||||||||||||||||||||||||||||||||
| checkDownloadedFiles: true | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - powershell: | | ||||||||||||||||||||||||||||||||||||||||||||
| $extensionPath = "$(Pipeline.Workspace)/extension/aspire-vscode-extension" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "=== VS Code Extension Inventory ===" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| $vsixFiles = Get-ChildItem -Path $extensionPath -Filter "*.vsix" -Recurse | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Found $($vsixFiles.Count) .vsix files:" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| foreach ($vsix in $vsixFiles) { | ||||||||||||||||||||||||||||||||||||||||||||
| $sizeMB = [math]::Round($vsix.Length / 1MB, 2) | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host " - $($vsix.Name) ($sizeMB MB)" | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if ($vsixFiles.Count -eq 0) { | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Error "No .vsix files found in artifacts!" | ||||||||||||||||||||||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # Check for signature files | ||||||||||||||||||||||||||||||||||||||||||||
| $manifestFiles = Get-ChildItem -Path $extensionPath -Filter "*.manifest" -Recurse | ||||||||||||||||||||||||||||||||||||||||||||
| $signatureFiles = Get-ChildItem -Path $extensionPath -Filter "*.signature.p7s" -Recurse | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Found $($manifestFiles.Count) manifest files" | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "Found $($signatureFiles.Count) signature files" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "===========================" | ||||||||||||||||||||||||||||||||||||||||||||
| displayName: 'List Extension Files' | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| - powershell: | | ||||||||||||||||||||||||||||||||||||||||||||
| $extensionPath = "$(Pipeline.Workspace)/extension/aspire-vscode-extension" | ||||||||||||||||||||||||||||||||||||||||||||
| $dryRun = [System.Convert]::ToBoolean("${{ parameters.DryRun }}") | ||||||||||||||||||||||||||||||||||||||||||||
| $preRelease = [System.Convert]::ToBoolean("${{ parameters.VSCodeExtensionPreRelease }}") | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| $vsixFiles = Get-ChildItem -Path $extensionPath -Filter "*.vsix" -Recurse | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "=== Publishing VS Code Extension to Marketplace ===" | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| if ($dryRun) { | ||||||||||||||||||||||||||||||||||||||||||||
| Write-Host "DRY RUN MODE - Extension will not actually be published" | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| foreach ($vsix in $vsixFiles) { | ||||||||||||||||||||||||||||||||||||||||||||
| $baseName = [System.IO.Path]::GetFileNameWithoutExtension($vsix.FullName) | ||||||||||||||||||||||||||||||||||||||||||||
| $manifestPath = Join-Path $extensionPath "$baseName.manifest" | ||||||||||||||||||||||||||||||||||||||||||||
| $signaturePath = Join-Path $extensionPath "$baseName.signature.p7s" | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+645
to
+646
|
||||||||||||||||||||||||||||||||||||||||||||
| $manifestPath = Join-Path $extensionPath "$baseName.manifest" | |
| $signaturePath = Join-Path $extensionPath "$baseName.signature.p7s" | |
| $vsixDirectory = $vsix.DirectoryName | |
| $manifestPath = Join-Path $vsixDirectory "$baseName.manifest" | |
| $signaturePath = Join-Path $vsixDirectory "$baseName.signature.p7s" |
Copilot
AI
Mar 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before running vsce verify-pat, consider explicitly validating that the VSCE_PAT environment variable is set/non-empty and failing with a clear message if it isn't. This makes failures easier to diagnose than relying on verify-pat's generic exit code.
Copilot
AI
Mar 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PAT verification is performed inside the loop for every .vsix. This adds redundant network calls and increases the chance of transient failures/rate limiting. Verify the PAT once before iterating through packages, then reuse that result for all publishes in the job.
| # Verify PAT is valid | |
| Write-Host "Verifying VS Code Marketplace PAT..." | |
| $publisher = "microsoft-aspire" | |
| npx vsce verify-pat $publisher | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Error "PAT verification failed for publisher '$publisher'. Ensure the token has 'Marketplace: Manage' scope." | |
| exit 1 | |
| } | |
| Write-Host "PAT verified successfully" | |
| # Verify PAT is valid once per job (cache result in script scope) | |
| if (-not $script:VscePatVerified) { | |
| Write-Host "Verifying VS Code Marketplace PAT..." | |
| $publisher = "microsoft-aspire" | |
| npx vsce verify-pat $publisher | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Error "PAT verification failed for publisher '$publisher'. Ensure the token has 'Marketplace: Manage' scope." | |
| exit 1 | |
| } | |
| Write-Host "PAT verified successfully" | |
| $script:VscePatVerified = $true | |
| } |
Copilot
AI
Mar 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script installs @vscode/vsce globally, but later uses npx vsce ... / npx for publishing. This can execute a different vsce than the one you pinned (or trigger an on-the-fly download), which makes the release less deterministic. Prefer invoking the installed vsce binary directly, or use npx @vscode/vsce@<pinnedVersion> consistently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this when we have
SkipExtensionPublish, which follows the pattern in this pipeline?