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
32 changes: 25 additions & 7 deletions .github/workflows/openvsx-publish-release-vsix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,28 @@ on:
required: false
default: false
type: boolean
secrets:
IDEE_OVSX_PAT:
description: Open VSX publishing token
required: true
vsix-name-pattern:
description: Glob of downloaded VSIX file names to publish
required: false
default: '*.vsix'
type: string
exclude-web-vsix:
description: Exclude *-web-* VSIXs from publishing
required: false
default: 'false'
type: string

permissions:
contents: read

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
# ovsx exchanges this job's OIDC ID token for a short-lived Open VSX publishing token
# (--trusted-publishing below) -- without this, there is no token to exchange.
id-token: write
steps:
- name: Download release VSIXs
id: release
Expand All @@ -33,8 +44,9 @@ jobs:
- name: Publish VSIXs
env:
DRY_RUN: ${{ inputs.dry-run }}
IDEE_OVSX_PAT: ${{ secrets.IDEE_OVSX_PAT }}
PRE_RELEASE: ${{ steps.release.outputs.pre-release }}
VSIX_PATTERN: ${{ inputs.vsix-name-pattern }}
EXCLUDE_WEB: ${{ inputs.exclude-web-vsix }}
run: |
# Open VSX can hang the same way as vsce; retry per VSIX.
# --skip-duplicate makes a retry a no-op if the first attempt committed.
Expand All @@ -54,7 +66,7 @@ jobs:
}

while IFS= read -r -d '' vsix; do
args=("$vsix" -p "$IDEE_OVSX_PAT" --skip-duplicate)
args=("$vsix" --trusted-publishing --skip-duplicate)
pre_release_suffix=''
if [ "$PRE_RELEASE" = true ]; then
args+=(--pre-release)
Expand All @@ -70,4 +82,10 @@ jobs:
echo "::error::Failed to publish $vsix after 3 attempts"
exit 1
fi
done < <(find extensions -type f -name '*.vsix' -print0)
done < <(
if [ "$EXCLUDE_WEB" = "true" ]; then
find extensions -type f -name "$VSIX_PATTERN" ! -iname '*-web-*' -print0
else
find extensions -type f -name "$VSIX_PATTERN" -print0
fi
)
25 changes: 23 additions & 2 deletions .github/workflows/vscode-manual-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ name: Manual Publish VS Code Extension
# - A nightly GitHub Release (version-tag path — normal workflow)
# - A branch CI build artifact (source-run-id path — out-of-band, bypass mandatory)
#
# This workflow does NOT publish Open VSX itself -- its `publish` job's ovsx leg is a
# deliberate no-op (see that job's comment). See "Open VSX Trusted Publishing" in this repo's
# README for why, and dispatch your own Open VSX publish after this workflow succeeds.
#
# Usage from consuming repository:
# jobs:
# manual-publish:
Expand All @@ -22,7 +26,7 @@ name: Manual Publish VS Code Extension
# secrets: inherit
#
# Requirements - calling repository must have:
# - Secrets: IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN, IDEE_OVSX_PAT
# - Secrets: IDEE_GH_TOKEN, VSCE_PERSONAL_ACCESS_TOKEN
# - For CBWeb: MARKETPLACE_DEPLOY_TOKEN and MARKETPLACE_URL variable
# - The package manager and lockfile inputs must match the caller repository.
# - The shared actions in this repository are resolved from the workflow branch.
Expand Down Expand Up @@ -317,6 +321,14 @@ jobs:
echo "ERROR: source-run-id path requires skip-quality-checks=true (branch CI profiles differ from main)."
exit 1
fi
# Open VSX publishing is tag-based (the caller dispatches publishOpenVSXTrusted.yml
# with a release tag -- see this workflow's header) and there is no GitHub Release,
# and therefore no tag, for a branch CI run artifact. Failing loudly here beats the
# alternative of silently never publishing to Open VSX for this combination.
if [ "$REGISTRIES" = "ovsx" ] || [ "$REGISTRIES" = "all" ]; then
echo "ERROR: source-run-id has no GitHub Release for Open VSX's tag-based trusted-publishing dispatch to target. Use registries=vsce for this path."
exit 1
fi
fi

# Bypass requires confirmation (case-insensitive)
Expand Down Expand Up @@ -794,7 +806,17 @@ jobs:
fi
echo "VSIX_PATH=$VSIX_FILE" >> $GITHUB_ENV

# Open VSX is NOT published here -- see this file's header. The caller dispatches
# publishOpenVSXTrusted.yml itself afterward instead, so this leg of the matrix (still
# present when registries is ovsx/all, so the job's own success/skip status -- and every
# downstream needs.publish.result == 'success' check -- stays exactly as it was) is a
# deliberate no-op rather than a removed step.
- name: Open VSX is published by the caller, not here
if: matrix.publish-tool == 'ovsx'
run: echo "Skipping -- the caller dispatches publishOpenVSXTrusted.yml for Open VSX. See this file's header comment."

- name: Publish to ${{ matrix.registry }}
if: matrix.publish-tool != 'ovsx'
uses: salesforcecli/github-workflows/.github/actions/publish-vsix@main
with:
vsix-path: ${{ env.VSIX_PATH }}
Expand All @@ -803,7 +825,6 @@ jobs:
dry-run: ${{ inputs.dry-run || 'false' }}
env:
VSCE_PERSONAL_ACCESS_TOKEN: ${{ secrets.VSCE_PERSONAL_ACCESS_TOKEN }}
OVSX_PAT: ${{ secrets.IDEE_OVSX_PAT }}

publish-to-cbweb-marketplace:
name: Publish to CBWeb Internal Marketplace
Expand Down
60 changes: 18 additions & 42 deletions .github/workflows/vscode-promote-prerelease.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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 "Open VSX Trusted Publishing" in this repo's README for why, and use this
# workflow's `release-tag` output to dispatch your own Open VSX publish afterward.
#
# 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.
Expand Down Expand Up @@ -36,9 +38,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 +122,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 +145,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 +169,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 +184,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 +203,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
41 changes: 28 additions & 13 deletions .github/workflows/vscode-promote-stable.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
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 "Open VSX Trusted Publishing"
# in this repo's README for why, and use this workflow's `release-tag` output to dispatch your
# own Open VSX publish afterward.
#
# Usage from consuming repository:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this belongs not in the workflow as a comment, but in the repo's docs. Look at it's top-level repo where it outlines various scenarios for npm, gh releases, etc. Maybe like that?

# jobs:
Expand All @@ -15,20 +18,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 +104,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 +351,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 +391,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 +420,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 +441,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
Loading