From 1b69fbe88ac49da7516c4fb7c3a7c700abd70a06 Mon Sep 17 00:00:00 2001 From: madhur310 Date: Mon, 26 Oct 2026 15:52:42 -0700 Subject: [PATCH 1/3] fix(release): add skip-ovsx opt-out to vscode-promote-prerelease.yml - W-24666922 A caller migrating Open VSX publishing to OIDC trusted publishing needs its ovsx publish step to run from the same top-level workflow file for every release channel (Open VSX's trusted-publisher match keys off the OIDC token's workflow_ref claim -- the run's initiating workflow -- and only allows one trusted-publisher registration per extension). This workflow's own inline ovsx publish, run as part of the caller's prerelease-promotion workflow, can't share that workflow_ref with a separate stable-release workflow. Adds an optional skip-ovsx input (default 'false', so every existing caller is unaffected) that lets a caller opt its ovsx leg out entirely, e.g. because it publishes Open VSX itself via a separate trusted-publishing dispatch instead. --- .../workflows/vscode-promote-prerelease.yml | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vscode-promote-prerelease.yml b/.github/workflows/vscode-promote-prerelease.yml index 3a35377..fabec4c 100644 --- a/.github/workflows/vscode-promote-prerelease.yml +++ b/.github/workflows/vscode-promote-prerelease.yml @@ -36,7 +36,9 @@ name: Promote Nightly to Pre-release # Requirements - calling repository must have: # - Nightly tags matching *-nightly.* (e.g., v1.2.3-nightly.20260629) # - GitHub releases for each nightly tag with VSIX file(s) attached -# - Secrets: IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN, IDEE_OVSX_PAT +# - Secrets: IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN +# - IDEE_OVSX_PAT, unless skip-ovsx: 'true' (e.g. the caller publishes Open VSX itself via a +# separate trusted-publishing dispatch, so its workflow_ref matches its stable release path) # # Workflow: downloads VSIX → publishes to both marketplaces → creates tracking tag @@ -71,6 +73,11 @@ on: required: false default: '22.x' type: string + skip-ovsx: + description: "Skip publishing to Open VSX from this workflow (e.g. because the caller publishes it separately via its own trusted-publishing dispatch)" + required: false + default: "false" + type: string workflow_dispatch: inputs: release-tag: @@ -107,6 +114,14 @@ on: required: false default: '22.x' type: string + skip-ovsx: + description: "Skip publishing to Open VSX from this workflow (e.g. because the caller publishes it separately via its own trusted-publishing dispatch)" + required: false + default: "false" + type: choice + options: + - "false" + - "true" concurrency: group: promote-prerelease @@ -158,6 +173,7 @@ jobs: EXCLUDE_WEB: ${{ inputs.exclude-web-vsix || 'false' }} PUBLISH_TOOL: ${{ matrix.publish-tool }} DRY_RUN: ${{ inputs.dry-run || 'false' }} + SKIP_OVSX: ${{ inputs.skip-ovsx || 'false' }} VSCE_PERSONAL_ACCESS_TOKEN: ${{ secrets.VSCE_PERSONAL_ACCESS_TOKEN }} OVSX_PAT: ${{ secrets.IDEE_OVSX_PAT }} run: | @@ -185,6 +201,13 @@ jobs: TOKEN="$VSCE_PERSONAL_ACCESS_TOKEN" fi + # Caller publishes Open VSX separately (e.g. via its own trusted-publishing dispatch) -- + # skip without even requiring OVSX_PAT to be set. + if [ "$PUBLISH_TOOL" = "ovsx" ] && [ "$SKIP_OVSX" = "true" ]; then + echo "⏭️ Skipping $MARKETPLACE_NAME -- caller publishes it separately (skip-ovsx input)." + exit 0 + fi + if [ "$DRY_RUN" != "true" ] && [ -z "$TOKEN" ]; then echo "::error::Token for $PUBLISH_TOOL is not set" exit 1 From 7cd24e9dd0b9cdc975d7f49aab7da015b6b30dec Mon Sep 17 00:00:00 2001 From: madhur310 Date: Tue, 27 Oct 2026 07:56:57 -0700 Subject: [PATCH 2/3] fix(release): remove Open VSX publishing from vscode-promote-prerelease.yml - W-24666922 Revises the previous commit's approach. A skip-ovsx toggle would have worked but leaves the PAT-based ovsx branch as permanent dead weight once every known caller migrates off it: vscode-promote-prerelease.yml has exactly two real callers (forcedotcom/salesforcedx-vscode, forcedotcom/apex-language-support), and both are moving their Open VSX publishing to a dedicated top-level workflow dispatched independently (see the file's new header comment for why: Open VSX's trusted-publisher match keys off workflow_ref, the run's initiating workflow, and allows only one registration per extension, so Open VSX publishing has to live in the same top-level file as each caller's stable release already uses). Removes the `ovsx` matrix leg, the skip-ovsx input, and IDEE_OVSX_PAT entirely rather than keeping an opt-out. Both callers' own migration PRs land in the same window as this one (coordinated merge order, not a runtime flag), so there's no gap where a caller's default branch would reference this file post-removal before its own compensating change is in place. --- .../workflows/vscode-promote-prerelease.yml | 89 ++++++------------- 1 file changed, 25 insertions(+), 64 deletions(-) diff --git a/.github/workflows/vscode-promote-prerelease.yml b/.github/workflows/vscode-promote-prerelease.yml index fabec4c..66d0ec5 100644 --- a/.github/workflows/vscode-promote-prerelease.yml +++ b/.github/workflows/vscode-promote-prerelease.yml @@ -1,10 +1,20 @@ name: Promote Nightly to Pre-release -# Publishes a nightly build to pre-release on VS Code Marketplace and Open VSX. +# Publishes a nightly build to pre-release on the VS Code Marketplace. Open VSX is NOT published +# from here -- see the note below. # # NOTE: Callers should use vscode-find-nightly-candidate.yml and check-ci-status action # BEFORE calling this workflow to implement their own gate-check logic. # +# NOTE on Open VSX: Open VSX's trusted-publisher match keys off the OIDC token's workflow_ref +# claim (the run's initiating workflow), and allows only one trusted-publisher registration per +# extension. A caller whose stable release publishes Open VSX from a different top-level workflow +# file than this one would get two different workflow_ref values for the same extension, which +# can't both be registered. So Open VSX publishing is each caller's own responsibility -- publish +# it from whichever single top-level workflow file you also use for your stable release's Open VSX +# publish (dispatched via `gh workflow run`, not `uses:`, so it's a genuinely new, independently +# initiated run and not nested inside this one). +# # Usage: # jobs: # find-nightly: @@ -37,10 +47,8 @@ name: Promote Nightly to Pre-release # - Nightly tags matching *-nightly.* (e.g., v1.2.3-nightly.20260629) # - GitHub releases for each nightly tag with VSIX file(s) attached # - Secrets: IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN -# - IDEE_OVSX_PAT, unless skip-ovsx: 'true' (e.g. the caller publishes Open VSX itself via a -# separate trusted-publishing dispatch, so its workflow_ref matches its stable release path) # -# Workflow: downloads VSIX → publishes to both marketplaces → creates tracking tag +# Workflow: downloads VSIX → publishes to VS Code Marketplace → creates tracking tag on: workflow_call: @@ -73,11 +81,6 @@ on: required: false default: '22.x' type: string - skip-ovsx: - description: "Skip publishing to Open VSX from this workflow (e.g. because the caller publishes it separately via its own trusted-publishing dispatch)" - required: false - default: "false" - type: string workflow_dispatch: inputs: release-tag: @@ -114,14 +117,6 @@ on: required: false default: '22.x' type: string - skip-ovsx: - description: "Skip publishing to Open VSX from this workflow (e.g. because the caller publishes it separately via its own trusted-publishing dispatch)" - required: false - default: "false" - type: choice - options: - - "false" - - "true" concurrency: group: promote-prerelease @@ -135,14 +130,6 @@ permissions: jobs: publish: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - registry: vsce - publish-tool: vsce - - registry: ovsx - publish-tool: ovsx steps: - name: Checkout uses: actions/checkout@v6 @@ -166,16 +153,13 @@ jobs: --dir ./vsix-artifacts \ --repo "${{ github.repository }}" - - name: Publish to ${{ matrix.registry }} + - name: Publish to Visual Studio Marketplace env: NIGHTLY_TAG: ${{ inputs.release-tag }} VSIX_PATTERN: ${{ inputs.vsix-name-pattern || '*.vsix' }} EXCLUDE_WEB: ${{ inputs.exclude-web-vsix || 'false' }} - PUBLISH_TOOL: ${{ matrix.publish-tool }} DRY_RUN: ${{ inputs.dry-run || 'false' }} - SKIP_OVSX: ${{ inputs.skip-ovsx || 'false' }} VSCE_PERSONAL_ACCESS_TOKEN: ${{ secrets.VSCE_PERSONAL_ACCESS_TOKEN }} - OVSX_PAT: ${{ secrets.IDEE_OVSX_PAT }} run: | set -uo pipefail @@ -193,27 +177,13 @@ jobs: exit 1 fi - if [ "$PUBLISH_TOOL" = "ovsx" ]; then - MARKETPLACE_NAME="Open VSX Registry" - TOKEN="$OVSX_PAT" - else - MARKETPLACE_NAME="Visual Studio Marketplace" - TOKEN="$VSCE_PERSONAL_ACCESS_TOKEN" - fi - - # Caller publishes Open VSX separately (e.g. via its own trusted-publishing dispatch) -- - # skip without even requiring OVSX_PAT to be set. - if [ "$PUBLISH_TOOL" = "ovsx" ] && [ "$SKIP_OVSX" = "true" ]; then - echo "⏭️ Skipping $MARKETPLACE_NAME -- caller publishes it separately (skip-ovsx input)." - exit 0 - fi - + TOKEN="$VSCE_PERSONAL_ACCESS_TOKEN" if [ "$DRY_RUN" != "true" ] && [ -z "$TOKEN" ]; then - echo "::error::Token for $PUBLISH_TOOL is not set" + echo "::error::Token for vsce is not set" exit 1 fi - echo "Publishing ${#VSIX_FILES[@]} VSIX(s) as pre-release to $MARKETPLACE_NAME:" + echo "Publishing ${#VSIX_FILES[@]} VSIX(s) as pre-release to Visual Studio Marketplace:" printf ' %s\n' "${VSIX_FILES[@]}" RUN_ID="${{ github.run_id }}" @@ -222,7 +192,7 @@ jobs: FAILED=0 # Marketplace gallery requests can sit until typed-rest-client's 180s - # socket timeout. vsce/ovsx have no timeout flag; retry per VSIX. + # socket timeout. vsce has no timeout flag; retry per VSIX. # --skip-duplicate makes a retry a no-op if the first attempt committed. publish_with_retry() { local attempt @@ -241,11 +211,11 @@ jobs: for VSIX in "${VSIX_FILES[@]}"; do NAME=$(basename "$VSIX") - echo "::group::$NAME → $MARKETPLACE_NAME" + echo "::group::$NAME → Visual Studio Marketplace" # Audit trail (mirrors the publish-vsix action's per-file record) HASH=$(sha256sum "$VSIX" 2>/dev/null | cut -d' ' -f1 || echo "unknown") - echo "[AUDIT] actor=$ACTOR repo=$REPO run_id=$RUN_ID tool=$PUBLISH_TOOL file=$NAME hash=$HASH pre_release=true dry_run=$DRY_RUN" + echo "[AUDIT] actor=$ACTOR repo=$REPO run_id=$RUN_ID tool=vsce file=$NAME hash=$HASH pre_release=true dry_run=$DRY_RUN" if [ "$DRY_RUN" = "true" ]; then echo "🔍 DRY RUN - would publish $NAME (pre-release)" @@ -253,29 +223,20 @@ jobs: continue fi - if [ "$PUBLISH_TOOL" = "vsce" ]; then - if VSCE_PAT="$TOKEN" publish_with_retry npx @vscode/vsce publish --packagePath "$VSIX" --skip-duplicate --pre-release; then - echo "✅ Published $NAME to $MARKETPLACE_NAME" - else - echo "::error::Failed to publish $NAME to $MARKETPLACE_NAME" - FAILED=1 - fi + if VSCE_PAT="$TOKEN" publish_with_retry npx @vscode/vsce publish --packagePath "$VSIX" --skip-duplicate --pre-release; then + echo "✅ Published $NAME to Visual Studio Marketplace" else - if publish_with_retry npx ovsx publish "$VSIX" -p "$TOKEN" --skip-duplicate --pre-release; then - echo "✅ Published $NAME to $MARKETPLACE_NAME" - else - echo "::error::Failed to publish $NAME to $MARKETPLACE_NAME" - FAILED=1 - fi + echo "::error::Failed to publish $NAME to Visual Studio Marketplace" + FAILED=1 fi echo "::endgroup::" done if [ "$FAILED" -ne 0 ]; then - echo "::error::One or more VSIX publishes failed for $PUBLISH_TOOL" + echo "::error::One or more VSIX publishes failed for vsce" exit 1 fi - echo "✅ All ${#VSIX_FILES[@]} VSIX(s) processed for $MARKETPLACE_NAME" + echo "✅ All ${#VSIX_FILES[@]} VSIX(s) processed for Visual Studio Marketplace" tag-promoted: needs: publish From ccd76133172b2f674c628136e2b8d12b60962d66 Mon Sep 17 00:00:00 2001 From: madhur310 Date: Tue, 27 Oct 2026 12:23:30 -0700 Subject: [PATCH 3/3] fix(release): remove Open VSX publishing from vscode-promote-stable.yml - W-24666922 Same reasoning as the prerelease workflow's removal: this file's only real caller (forcedotcom/apex-language-support) is migrating its Open VSX publishing to a dedicated top-level workflow dispatched independently, for the same trusted-publisher workflow_ref reason documented in both files' headers. Removes the `ovsx` matrix leg and IDEE_OVSX_PAT. Also adds a `release-tag` output (surfaced from create-github-release's computed tag) so a caller can dispatch its own Open VSX publish for the exact tag this run just created, without having to recompute it. --- .github/workflows/vscode-promote-stable.yml | 49 +++++++++++++++------ 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/.github/workflows/vscode-promote-stable.yml b/.github/workflows/vscode-promote-stable.yml index b141c95..76e1352 100644 --- a/.github/workflows/vscode-promote-stable.yml +++ b/.github/workflows/vscode-promote-stable.yml @@ -1,6 +1,17 @@ name: Promote Pre-release to Stable -# Reusable workflow for promoting a pre-release VS Code extension to stable. +# Reusable workflow for promoting a pre-release VS Code extension to stable. Publishes to the VS +# Code Marketplace only -- Open VSX is NOT published from here, see the note below. +# +# NOTE on Open VSX: Open VSX's trusted-publisher match keys off the OIDC token's workflow_ref +# claim (the run's initiating workflow), and allows only one trusted-publisher registration per +# extension. A caller whose prerelease publishes Open VSX from a different top-level workflow +# file than this one would get two different workflow_ref values for the same extension, which +# can't both be registered. So Open VSX publishing is each caller's own responsibility -- publish +# it from whichever single top-level workflow file you also use for your prerelease's Open VSX +# publish, using this workflow's `release-tag` output to know which tag to publish (dispatched via +# `gh workflow run`, not `uses:`, so it's a genuinely new, independently initiated run and not +# nested inside this one). # # Usage from consuming repository: # jobs: @@ -15,12 +26,20 @@ name: Promote Pre-release to Stable # dry-run: 'false' # secrets: inherit # +# publish-openvsx: +# needs: promote +# runs-on: ubuntu-latest +# env: +# GH_TOKEN: ${{ secrets.IDEE_GH_TOKEN }} +# steps: +# - run: gh workflow run publishOpenVSXTrusted.yml --repo "${{ github.repository }}" -f release-tag="${{ needs.promote.outputs.release-tag }}" +# # Requirements - calling repository must have: # - marketplace-prerelease-* tracking tags # - Nightly tags matching v{version}-nightly.* pattern # - GitHub releases for each nightly tag with VSIX file(s) attached # - Passing CI checks on the nightly candidate commit -# - Secrets: IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN, IDEE_OVSX_PAT +# - Secrets: IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN # - The shared actions in this repository are resolved from the workflow branch. # - The package manager and lockfile inputs must match the caller repository. # @@ -28,7 +47,7 @@ name: Promote Pre-release to Stable # resolved from this repository so callers do not need duplicate composite actions. # # Workflow: finds latest pre-release → verifies quality gate → repackages VSIX -# → publishes to both marketplaces → creates GitHub release → tags stable +# → publishes to VS Code Marketplace → creates GitHub release → tags stable # → commits stable version back to main on: @@ -93,6 +112,10 @@ on: required: false default: 'npm ci' type: string + outputs: + release-tag: + description: 'The stable GitHub release tag this run created (e.g. apex-language-server-extension-v1.2.3). Use this to dispatch your own Open VSX trusted-publishing workflow afterward -- see the header comment above.' + value: ${{ jobs.create-github-release.outputs.release-tag }} workflow_dispatch: inputs: extension-name: @@ -336,16 +359,13 @@ jobs: name: stable-vsix path: ${{ steps.repack.outputs.vsix-path }} + # Open VSX is NOT published from here -- see this file's header for why (trusted-publisher + # match keys off workflow_ref, the run's initiating workflow, and allows only one registration + # per extension, so it has to live in whichever single top-level workflow file the caller also + # uses for its prerelease's Open VSX publish). publish: needs: [find-prerelease-candidate, repackage] runs-on: ubuntu-latest - strategy: - matrix: - include: - - registry: vsce - publish-tool: vsce - - registry: ovsx - publish-tool: ovsx steps: - name: Checkout uses: actions/checkout@v6 @@ -379,20 +399,21 @@ jobs: fi echo "VSIX_PATH=$VSIX_FILE" >> $GITHUB_ENV - - name: Publish to ${{ matrix.registry }} + - name: Publish to Visual Studio Marketplace uses: salesforcecli/github-workflows/.github/actions/publish-vsix@main with: vsix-path: ${{ env.VSIX_PATH }} - publish-tool: ${{ matrix.publish-tool }} + publish-tool: vsce pre-release: 'false' dry-run: ${{ inputs.dry-run || 'false' }} env: VSCE_PERSONAL_ACCESS_TOKEN: ${{ secrets.VSCE_PERSONAL_ACCESS_TOKEN }} - OVSX_PAT: ${{ secrets.IDEE_OVSX_PAT }} create-github-release: needs: [find-prerelease-candidate, repackage, publish] runs-on: ubuntu-latest + outputs: + release-tag: ${{ steps.release.outputs.release-tag }} steps: - name: Checkout uses: actions/checkout@v6 @@ -407,6 +428,7 @@ jobs: path: ./vsix-artifacts - name: Create GitHub release for stable version + id: release env: GH_TOKEN: ${{ secrets.IDEE_GH_TOKEN }} STABLE_VERSION: ${{ needs.find-prerelease-candidate.outputs.stable-version }} @@ -427,6 +449,7 @@ jobs: # e.g., apex-language-server-extension-1.2.3.vsix -> apex-language-server-extension BASE_NAME=$(basename "$VSIX_FILE" .vsix | sed -E 's/-[0-9]+\.[0-9]+\.[0-9]+$//') RELEASE_TAG="${BASE_NAME}-v${STABLE_VERSION}" + echo "release-tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" RELEASE_TITLE="${BASE_NAME} v${STABLE_VERSION} (stable)" RELEASE_NOTES="## Stable Release v${STABLE_VERSION}