Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 26 additions & 42 deletions .github/workflows/vscode-promote-prerelease.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -36,9 +46,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
#
# Workflow: downloads VSIX → publishes to both marketplaces → creates tracking tag
# Workflow: downloads VSIX → publishes to VS Code Marketplace → creates tracking tag

on:
workflow_call:
Expand Down Expand Up @@ -120,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
Expand All @@ -151,15 +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' }}
VSCE_PERSONAL_ACCESS_TOKEN: ${{ secrets.VSCE_PERSONAL_ACCESS_TOKEN }}
OVSX_PAT: ${{ secrets.IDEE_OVSX_PAT }}
run: |
set -uo pipefail

Expand All @@ -177,20 +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

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 }}"
Expand All @@ -199,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
Expand All @@ -218,41 +211,32 @@ 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)"
echo "::endgroup::"
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
Expand Down
49 changes: 36 additions & 13 deletions .github/workflows/vscode-promote-stable.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -15,20 +26,28 @@ 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.
#
# The caller supplies credentials and repository-specific paths; action implementations are
# 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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 }}
Expand All @@ -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}

Expand Down