Extensions - Release #46
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' # Tag push: build + GitHub release only (no marketplace publish) | |
| workflow_dispatch: # Manual trigger: full pipeline including marketplace publish | |
| inputs: | |
| create_tag: | |
| description: 'Create tag from package.json version' | |
| required: false | |
| default: true | |
| type: boolean | |
| publish_marketplace: | |
| description: 'Publish to VS Code Marketplace after creating the release' | |
| required: false | |
| default: true | |
| type: boolean | |
| permissions: | |
| contents: read | |
| jobs: | |
| release: | |
| name: GitHub Release | |
| permissions: | |
| contents: write | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.extract_version.outputs.tag_version }} | |
| tag_name: ${{ steps.tag_name.outputs.tag_name }} | |
| vsix_file: ${{ steps.vsix_filename.outputs.vsix_file }} | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: '20.x' | |
| cache: 'npm' | |
| cache-dependency-path: vscode-extension/package-lock.json | |
| - name: Determine trigger type | |
| id: trigger_type | |
| run: | | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| echo "is_manual=true" >> "$GITHUB_OUTPUT" | |
| echo "Triggered manually via workflow_dispatch" | |
| else | |
| echo "is_manual=false" >> "$GITHUB_OUTPUT" | |
| echo "Triggered by tag push" | |
| fi | |
| - name: Extract version from package.json | |
| id: package_version | |
| run: | | |
| PACKAGE_VERSION=$(node -p "require('./vscode-extension/package.json').version") | |
| echo "package_version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Package version: $PACKAGE_VERSION" | |
| - name: Extract version from tag | |
| id: extract_version | |
| run: | | |
| if [ "${{ steps.trigger_type.outputs.is_manual }}" == "true" ]; then | |
| # For manual triggers, use package.json version | |
| TAG_VERSION="${{ steps.package_version.outputs.package_version }}" | |
| else | |
| # For tag triggers, extract from the tag | |
| TAG_VERSION=${GITHUB_REF#refs/tags/v} | |
| fi | |
| echo "tag_version=$TAG_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Release version: $TAG_VERSION" | |
| - name: Verify version consistency | |
| run: | | |
| if [ "${{ steps.extract_version.outputs.tag_version }}" != "${{ steps.package_version.outputs.package_version }}" ]; then | |
| echo "❌ Version mismatch!" | |
| echo "Tag version: ${{ steps.extract_version.outputs.tag_version }}" | |
| echo "Package.json version: ${{ steps.package_version.outputs.package_version }}" | |
| echo "Please ensure the tag version matches the version in package.json" | |
| exit 1 | |
| fi | |
| echo "✅ Version check passed: ${{ steps.extract_version.outputs.tag_version }}" | |
| - name: Determine tag name | |
| id: tag_name | |
| run: | | |
| if [ "${{ steps.trigger_type.outputs.is_manual }}" == "true" ]; then | |
| TAG_NAME="v${{ steps.extract_version.outputs.tag_version }}" | |
| else | |
| TAG_NAME="${{ github.ref_name }}" | |
| fi | |
| echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" | |
| echo "Tag name: $TAG_NAME" | |
| - name: Generate release notes | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG_NAME="${{ steps.tag_name.outputs.tag_name }}" | |
| VERSION="${{ steps.extract_version.outputs.tag_version }}" | |
| echo "Generating release notes for $TAG_NAME..." | |
| # Use GitHub API to auto-generate notes from merged PRs since last release | |
| if gh api repos/${{ github.repository }}/releases/generate-notes \ | |
| -f tag_name="${TAG_NAME}" \ | |
| --jq '.body' > /tmp/release_notes.md 2>/tmp/generate_notes_err.txt && [ -s /tmp/release_notes.md ]; then | |
| echo "✅ Generated release notes from merged PRs" | |
| else | |
| echo "Release ${VERSION}" > /tmp/release_notes.md | |
| echo "⚠️ Could not auto-generate notes, using fallback" | |
| if [ -s /tmp/generate_notes_err.txt ]; then | |
| echo " Error: $(cat /tmp/generate_notes_err.txt)" | |
| fi | |
| fi | |
| echo "--- Release notes preview ---" | |
| cat /tmp/release_notes.md | |
| echo "---" | |
| - name: Update CHANGELOG.md for VSIX packaging | |
| run: | | |
| VERSION="${{ steps.extract_version.outputs.tag_version }}" | |
| node -e " | |
| const fs = require('fs'); | |
| const version = process.argv[1]; | |
| const notes = fs.readFileSync('/tmp/release_notes.md', 'utf8').trim(); | |
| let changelog = fs.readFileSync('CHANGELOG.md', 'utf8'); | |
| const marker = '## [Unreleased]'; | |
| const idx = changelog.indexOf(marker); | |
| if (idx >= 0) { | |
| const insertAt = changelog.indexOf('\n', idx) + 1; | |
| const section = '\n## [' + version + ']\n\n' + notes + '\n'; | |
| changelog = changelog.slice(0, insertAt) + section + changelog.slice(insertAt); | |
| } | |
| fs.writeFileSync('CHANGELOG.md', changelog); | |
| console.log('✅ Updated CHANGELOG.md with v' + version + ' notes for VSIX packaging'); | |
| " "$VERSION" | |
| - name: Create changelog branch and commit | |
| if: steps.trigger_type.outputs.is_manual == 'true' | |
| id: changelog_branch | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ steps.extract_version.outputs.tag_version }}" | |
| BRANCH="changelog/v${VERSION}" | |
| echo "branch=$BRANCH" >> "$GITHUB_OUTPUT" | |
| git config --local user.name "github-actions[bot]" | |
| git config --local user.email "github-actions[bot]@users.noreply.github.com" | |
| # Check if branch already exists on remote | |
| if git ls-remote --exit-code --heads origin "refs/heads/$BRANCH" >/dev/null 2>&1; then | |
| MSG="❌ Branch $BRANCH already exists on remote! Delete it or bump the version before re-running." | |
| echo "$MSG" | |
| echo "$MSG" > /tmp/changelog_error.txt | |
| exit 1 | |
| fi | |
| if git diff --quiet CHANGELOG.md; then | |
| echo "ℹ️ No changes to CHANGELOG.md, skipping branch + commit" | |
| exit 0 | |
| fi | |
| git checkout -b "$BRANCH" | |
| git add CHANGELOG.md | |
| git commit -m "docs: update CHANGELOG.md for v${VERSION}" | |
| git push origin "$BRANCH" | |
| echo "✅ Pushed CHANGELOG.md update to $BRANCH" | |
| - name: Create tag for manual trigger | |
| if: steps.trigger_type.outputs.is_manual == 'true' && inputs.create_tag == true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="v${{ steps.package_version.outputs.package_version }}" | |
| echo "Creating tag: $VERSION" | |
| # Check if tag already exists on remote using exit code | |
| if git ls-remote --exit-code --tags origin "refs/tags/$VERSION" >/dev/null 2>&1; then | |
| echo "❌ Tag $VERSION already exists on remote!" | |
| echo "Please update the version in package.json or delete the existing tag." | |
| exit 1 | |
| fi | |
| # Create and push the tag | |
| git config --local user.name "github-actions[bot]" | |
| git config --local user.email "github-actions[bot]@users.noreply.github.com" | |
| git tag -a "$VERSION" -m "Release $VERSION" | |
| git push origin "$VERSION" | |
| # Verify tag was created successfully on remote | |
| echo "Verifying tag was created on remote..." | |
| for i in {1..5}; do | |
| if git ls-remote --exit-code --tags origin "refs/tags/$VERSION" >/dev/null 2>&1; then | |
| echo "✅ Tag $VERSION created and verified on remote" | |
| exit 0 | |
| fi | |
| echo "Waiting for tag to propagate (attempt $i/5)..." | |
| sleep 2 | |
| done | |
| echo "❌ Failed to verify tag creation on remote" | |
| exit 1 | |
| - name: Install dependencies | |
| working-directory: vscode-extension | |
| run: npm ci | |
| - name: Run linting | |
| working-directory: vscode-extension | |
| run: npm run lint | |
| - name: Run type checking | |
| working-directory: vscode-extension | |
| run: npm run check-types | |
| - name: Compile extension | |
| working-directory: vscode-extension | |
| run: npm run compile | |
| - name: Build production package | |
| working-directory: vscode-extension | |
| run: npm run package | |
| - name: Compile tests | |
| working-directory: vscode-extension | |
| run: npm run compile-tests | |
| - name: Run tests | |
| uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1.0.1 | |
| with: | |
| run: npm test | |
| options: -screen 0 1024x768x24 | |
| working-directory: vscode-extension | |
| continue-on-error: false # Fail the release if tests fail | |
| - name: Create VSIX package | |
| working-directory: vscode-extension | |
| run: npx vsce package | |
| - name: Get VSIX filename | |
| id: vsix_filename | |
| working-directory: vscode-extension | |
| run: | | |
| VSIX_FILE=$(find . -maxdepth 1 -name "*.vsix" -exec basename {} \; | head -n 1) | |
| echo "vsix_file=$VSIX_FILE" >> "$GITHUB_OUTPUT" | |
| echo "VSIX file: $VSIX_FILE" | |
| - name: Upload VSIX as workflow artifact | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: vsix-package | |
| path: vscode-extension/${{ steps.vsix_filename.outputs.vsix_file }} | |
| retention-days: 90 | |
| - name: Create Release | |
| id: create_release | |
| shell: bash | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| continue-on-error: true | |
| run: | | |
| set -o pipefail | |
| echo "Creating release for tag: ${{ steps.tag_name.outputs.tag_name }}" | |
| # Create release with auto-generated notes and upload VSIX file | |
| gh release create "${{ steps.tag_name.outputs.tag_name }}" \ | |
| --title "Release ${{ steps.extract_version.outputs.tag_version }}" \ | |
| --notes-file /tmp/release_notes.md \ | |
| vscode-extension/${{ steps.vsix_filename.outputs.vsix_file }} 2>&1 | tee /tmp/release_output.txt | |
| - name: Release Summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| if [ "${{ steps.create_release.outcome }}" == "success" ]; then | |
| { | |
| echo "# ✅ Release Created Successfully" | |
| echo "" | |
| echo "🎉 Release ${{ steps.extract_version.outputs.tag_version }} created successfully!" | |
| echo "📦 VSIX package: ${{ steps.vsix_filename.outputs.vsix_file }}" | |
| echo "🔗 [Release URL](https://github.com/${{ github.repository }}/releases/tag/${{ steps.tag_name.outputs.tag_name }})" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| { | |
| echo "# ❌ Release Failed" | |
| echo "" | |
| echo "**Version:** ${{ steps.extract_version.outputs.tag_version }}" | |
| echo "**Tag:** ${{ steps.tag_name.outputs.tag_name }}" | |
| echo "" | |
| echo "## Error Details" | |
| echo "" | |
| echo "\`\`\`" | |
| if [ -f /tmp/changelog_error.txt ]; then | |
| cat /tmp/changelog_error.txt | |
| elif [ -f /tmp/release_output.txt ]; then | |
| cat /tmp/release_output.txt | |
| else | |
| echo "No error details captured — check the job logs above for the failed step." | |
| fi | |
| echo "\`\`\`" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| fi | |
| publish: | |
| needs: release | |
| name: Publish VS Code Extension | |
| if: github.event_name == 'workflow_dispatch' && inputs.publish_marketplace | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Check marketplace version | |
| id: version_check | |
| run: | | |
| LOCAL_VERSION="${{ needs.release.outputs.version }}" | |
| echo "Local version: $LOCAL_VERSION" | |
| MARKETPLACE_VERSION=$(curl -s -X POST \ | |
| "https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery?api-version=3.0-preview.1" \ | |
| -H "Content-Type: application/json" \ | |
| -H "Accept: application/json;api-version=3.0-preview.1" \ | |
| -d '{"filters":[{"criteria":[{"filterType":7,"value":"RobBos.copilot-token-tracker"}]}],"flags":512}' \ | |
| | jq -r '.results[0].extensions[0].versions[0].version // empty') | |
| if [ -z "$MARKETPLACE_VERSION" ]; then | |
| echo "⚠️ Could not retrieve marketplace version — proceeding with publish." | |
| exit 0 | |
| fi | |
| echo "Marketplace version: $MARKETPLACE_VERSION" | |
| if [ "$LOCAL_VERSION" == "$MARKETPLACE_VERSION" ]; then | |
| echo "❌ Version $LOCAL_VERSION is already published on the VS Code Marketplace." | |
| echo " Bump the version in vscode-extension/package.json before publishing." | |
| exit 1 | |
| fi | |
| echo "✅ Version check passed: $LOCAL_VERSION is newer than marketplace $MARKETPLACE_VERSION" | |
| - name: Setup Node.js | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: '20.x' | |
| cache: 'npm' | |
| cache-dependency-path: vscode-extension/package-lock.json | |
| - name: Install dependencies | |
| working-directory: vscode-extension | |
| run: npm ci | |
| - name: Download VSIX from release | |
| id: download_vsix | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG_NAME="${{ needs.release.outputs.tag_name }}" | |
| echo "Downloading VSIX from release $TAG_NAME..." | |
| gh release download "$TAG_NAME" \ | |
| --repo "${{ github.repository }}" \ | |
| --pattern "*.vsix" \ | |
| --dir . | |
| VSIX_FILE=$(find . -maxdepth 1 -name "*.vsix" -exec basename {} \; | head -n 1) | |
| echo "Downloaded: $VSIX_FILE" | |
| echo "vsix_file=$VSIX_FILE" >> "$GITHUB_OUTPUT" | |
| - name: Publish to VS Code Marketplace | |
| id: publish | |
| env: | |
| VSCE_PAT: ${{ secrets.VSCE_PAT }} | |
| run: | | |
| if [ -z "$VSCE_PAT" ]; then | |
| echo "❌ VSCE_PAT secret is not configured." | |
| echo " Add it at: Settings → Secrets and variables → Actions" | |
| echo " Create a PAT at https://dev.azure.com with 'Marketplace (Publish)' scope" | |
| exit 1 | |
| fi | |
| echo "Publishing ${{ steps.download_vsix.outputs.vsix_file }} to VS Code Marketplace..." | |
| npx vsce publish \ | |
| --packagePath "${{ steps.download_vsix.outputs.vsix_file }}" \ | |
| --pat "$VSCE_PAT" | |
| - name: Publish Summary | |
| if: always() | |
| run: | | |
| { | |
| echo "# 🚀 VS Code Marketplace" | |
| echo "" | |
| if [ "${{ steps.publish.outcome }}" == "success" ]; then | |
| echo "✅ Extension v${{ needs.release.outputs.version }} published to the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=RobBos.copilot-token-tracker)" | |
| elif [ "${{ steps.version_check.outcome }}" == "failure" ]; then | |
| echo "⏭️ Publish skipped — v${{ needs.release.outputs.version }} is already live on the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=RobBos.copilot-token-tracker)." | |
| echo "" | |
| echo "Bump the version in \`vscode-extension/package.json\` before publishing again." | |
| else | |
| echo "❌ Failed to publish v${{ needs.release.outputs.version }} to marketplace." | |
| echo "" | |
| echo "Ensure the \`VSCE_PAT\` secret is configured with a valid Azure DevOps PAT" | |
| echo "with the \`Marketplace (Publish)\` scope for all accessible organizations." | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| build-visualstudio: | |
| name: Publish Visual Studio Extension | |
| needs: release | |
| # Windows required: Node.js SEA (bundle-exe.ps1) and MSBuild/VSSDK are Windows-only | |
| runs-on: windows-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: '22.x' | |
| # ── Install dependencies ──────────────────────────────────────────────── | |
| - name: Install vscode-extension dependencies | |
| run: npm ci | |
| working-directory: vscode-extension | |
| - name: Install CLI dependencies | |
| run: npm ci | |
| working-directory: cli | |
| # ── Build CLI (production bundle + Windows .exe) ─────────────────────── | |
| - name: Build CLI (production bundle) | |
| working-directory: cli | |
| run: npm run build:production | |
| - name: Bundle CLI as single executable | |
| working-directory: cli | |
| shell: pwsh | |
| run: | | |
| & pwsh -NoProfile -File bundle-exe.ps1 -SkipBuild | |
| if ($LASTEXITCODE -ne 0) { throw "bundle-exe.ps1 failed" } | |
| - name: Copy CLI exe + wasm to cli-bundle/ | |
| shell: pwsh | |
| run: | | |
| $vsCliDir = "visualstudio-extension\src\CopilotTokenTracker\cli-bundle" | |
| New-Item -ItemType Directory -Path $vsCliDir -Force | Out-Null | |
| Copy-Item "cli\dist\copilot-token-tracker.exe" "$vsCliDir\copilot-token-tracker.exe" -Force | |
| Copy-Item "cli\dist\sql-wasm.wasm" "$vsCliDir\sql-wasm.wasm" -Force | |
| Write-Host "✅ Copied cli-bundle assets" | |
| # ── Build webview bundles ────────────────────────────────────────────── | |
| - name: Build VS Code extension webview bundles | |
| working-directory: vscode-extension | |
| run: npm run package | |
| - name: Copy webview bundles to VS extension project | |
| shell: pwsh | |
| run: | | |
| $src = "vscode-extension\dist\webview" | |
| $dst = "visualstudio-extension\src\CopilotTokenTracker\webview" | |
| if (Test-Path $dst) { Remove-Item $dst -Recurse -Force } | |
| Copy-Item $src $dst -Recurse -Force | |
| Write-Host "✅ Copied webview bundles" | |
| # ── Build Visual Studio extension (MSBuild / VSSDK) ─────────────────── | |
| - name: Add MSBuild to PATH | |
| uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 | |
| - name: Restore NuGet packages | |
| working-directory: visualstudio-extension | |
| run: nuget restore CopilotTokenTracker.sln | |
| - name: Build solution (Release) | |
| working-directory: visualstudio-extension | |
| run: msbuild CopilotTokenTracker.sln /p:Configuration=Release /t:Build /v:minimal | |
| # ── Collect the produced .vsix ───────────────────────────────────────── | |
| - name: Find .vsix artifact | |
| id: vsix | |
| shell: pwsh | |
| run: | | |
| $vsix = Get-ChildItem -Path "visualstudio-extension" -Filter "*.vsix" -Recurse | | |
| Sort-Object LastWriteTime -Descending | | |
| Select-Object -First 1 | |
| if (-not $vsix) { throw "No .vsix file produced" } | |
| $sizeMB = [math]::Round($vsix.Length / 1MB, 1) | |
| Write-Host "✅ VSIX: $($vsix.FullName) ($sizeMB MB)" | |
| echo "vsix_path=$($vsix.FullName)" >> $env:GITHUB_OUTPUT | |
| echo "vsix_name=$($vsix.Name)" >> $env:GITHUB_OUTPUT | |
| # ── Upload .vsix to GitHub release ──────────────────────────────────── | |
| - name: Upload .vsix to GitHub release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: pwsh | |
| run: | | |
| $tag = "${{ needs.release.outputs.tag_name }}" | |
| $vsixPath = "${{ steps.vsix.outputs.vsix_path }}" | |
| Write-Host "Uploading $vsixPath to release $tag ..." | |
| gh release upload $tag $vsixPath --clobber | |
| if ($LASTEXITCODE -ne 0) { throw "Failed to upload .vsix to release" } | |
| Write-Host "✅ Uploaded to GitHub release" | |
| # ── (Optional) Publish to VS Marketplace ────────────────────────────── | |
| - name: Publish to Visual Studio Marketplace | |
| if: github.event_name == 'workflow_dispatch' && inputs.publish_marketplace == true | |
| shell: pwsh | |
| env: | |
| VS_MARKETPLACE_PAT: ${{ secrets.VSCE_PAT }} # same one for the VSCode Marketplace works for Visual Studio Marketplace as well | |
| run: | | |
| if (-not $env:VS_MARKETPLACE_PAT) { | |
| Write-Error "❌ VS_MARKETPLACE_PAT secret is not configured." | |
| exit 1 | |
| } | |
| # VsixPublisher.exe is the correct tool for Visual Studio IDE extensions | |
| # (not @vscode/vsce, which is for VS Code extensions only) | |
| $vsixPublisher = Get-ChildItem "C:\Program Files\Microsoft Visual Studio" ` | |
| -Recurse -Filter "VsixPublisher.exe" -ErrorAction SilentlyContinue | | |
| Select-Object -First 1 | |
| if (-not $vsixPublisher) { | |
| Write-Error "❌ VsixPublisher.exe not found on this runner." | |
| exit 1 | |
| } | |
| Write-Host "Found: $($vsixPublisher.FullName)" | |
| $vsix = "${{ steps.vsix.outputs.vsix_path }}" | |
| $manifest = "visualstudio-extension/publish-manifest.json" | |
| Write-Host "Publishing $vsix to Visual Studio Marketplace..." | |
| & $vsixPublisher.FullName publish -payload $vsix -publishManifest $manifest -personalAccessToken $env:VS_MARKETPLACE_PAT | |
| if ($LASTEXITCODE -ne 0) { throw "Marketplace publish failed" } | |
| Write-Host "✅ Published to Visual Studio Marketplace" | |
| # ── Summary ──────────────────────────────────────────────────────────── | |
| - name: Build summary | |
| if: always() | |
| shell: pwsh | |
| run: | | |
| $vsixName = "${{ steps.vsix.outputs.vsix_name }}" | |
| if ($vsixName) { | |
| @" | |
| ## ✅ Visual Studio Extension Built Successfully | |
| | Item | Value | | |
| |------|-------| | |
| | VSIX | ``$vsixName`` | | |
| | Release | ``${{ needs.release.outputs.tag_name }}`` | | |
| | Commit | ``${{ github.sha }}`` | | |
| "@ >> $env:GITHUB_STEP_SUMMARY | |
| } else { | |
| "## ❌ Build failed — no .vsix produced" >> $env:GITHUB_STEP_SUMMARY | |
| } | |