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
)
31 changes: 29 additions & 2 deletions .github/workflows/vscode-manual-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ 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)
#
# NOTE on Open VSX: this workflow does NOT publish Open VSX itself, even when registries is
# ovsx/all -- its `publish` job's ovsx leg is a deliberate no-op (see that job's comment). 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, so
# Open VSX publishing has to live in whichever single top-level workflow file the caller also
# uses for its other release channels. The caller is expected to dispatch that file itself (via
# `gh workflow run`, not `uses:`) after this workflow succeeds, for the version-tag path only --
# source-run-id has no GitHub Release for a tag-based publish to target, so `prepare` hard-errors
# that combination instead of silently never publishing to Open VSX.
#
# Usage from consuming repository:
# jobs:
# manual-publish:
Expand All @@ -22,7 +32,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 +327,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 +812,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 +831,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
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
Loading