Skip to content

fix(release): migrate Open VSX publishing to OIDC trusted publishing - W-24666922 - #199

Merged
madhur310 merged 5 commits into
mainfrom
ms/W-24666922-ovsx-trusted-publishing
Nov 10, 2026
Merged

madhur310 merged 5 commits into
mainfrom
ms/W-24666922-ovsx-trusted-publishing

Conversation

@madhur310

@madhur310 madhur310 commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • openvsx-publish-release-vsix.yml: migrates ovsx publish from a PAT (-p $IDEE_OVSX_PAT) to ovsx publish --trusted-publishing (requires id-token: write, now granted). Adds optional vsix-name-pattern/exclude-web-vsix inputs (behavior-preserving defaults).
  • vscode-promote-prerelease.yml / vscode-promote-stable.yml: removes the ovsx matrix leg entirely (no toggle) — IDEE_OVSX_PAT no longer required by either. Adds a release-tag output to vscode-promote-stable.yml (the stable tag it just created).
  • vscode-manual-publish.yml: its publish job's ovsx leg becomes a deliberate no-op step (matrix entry and job status unchanged, so every downstream needs.publish.result == 'success' check in this file is unaffected). Adds a hard error in prepare for source-run-id + registries: ovsx/all, since that path has no GitHub Release for a tag-based trusted-publishing dispatch to target.

(Previously split across this PR and #200 — folded into one here since all of these are the same migration and it's simpler to coordinate one github-workflows branch against each calling repo's own migration PR.)

Context

Open VSX's trusted-publisher match keys off the OIDC token's workflow_ref claim — the run's initiating workflow — not job_workflow_ref (the reusable workflow actually executing a job, which can be nested arbitrarily deep via uses:, even cross-repo, without changing workflow_ref). Open VSX also allows exactly one trusted-publisher registration per extension, enforced server-side (TrustedPublishingService.registerTrustedPublisher in eclipse-openvsx/openvsx) — not just a UI limitation.

Each caller of these reusable workflows was publishing Open VSX from these files' inline PAT-based leg, while also having one or more other top-level workflows for its other release channels — different workflow_ref values for the same extension, which can't all be registered. forcedotcom/salesforcedx-vscode has two such channels (stable, prerelease); forcedotcom/apex-language-support has three (prerelease, stable, and a manual/break-glass publish path). Each caller is migrating Open VSX publishing onto one dedicated top-level workflow that every one of its channels dispatches/triggers directly (not nested via uses:), delegating the actual publish step to this PR's openvsx-publish-release-vsix.yml. That makes removing/redirecting the old PAT-based legs here required, not just cleanup — otherwise every publish double-publishes to Open VSX and IDEE_OVSX_PAT can never be retired.

Validation

  • actionlint on all four changed files: zero new findings vs. main (confirmed via git stash/diff — same pre-existing shellcheck notices as before, exact counts matched).
  • Traced every real caller of all four files via GitHub code search (vscode-promote-prerelease.yml: 2 callers; vscode-promote-stable.yml and vscode-manual-publish.yml: 1 caller each; openvsx-publish-release-vsix.yml: 1 caller) to confirm none are left unaccounted for.
  • End-to-end confirmed against both calling repos, dispatching each one's publishOpenVSXTrusted.yml (pinned to this branch) for real against an already-published, --skip-duplicate-safe tag — no new content reached the registry:
    • forcedotcom/apex-language-support (1 extension): run 41496313940 / publish job — 🔐 Trusted publishing token issued for salesforce.apex-lsp-vscode-extension logged, publish step completed via --skip-duplicate.
    • forcedotcom/salesforcedx-vscode (all 17 Open VSX-published extensions): run 41499926285 / publish job — trusted publishing token issued for every extension, zero ❌ No trusted publisher matches errors.

Test plan

  • forcedotcom/salesforcedx-vscode's and forcedotcom/apex-language-support's migration PRs validate end-to-end against this branch before merge
  • Coordinate merge order: this PR merges alongside/after each caller's compensating PR
  • Register the Open VSX trusted publisher for each affected extension against its new top-level workflow file
  • Confirm a real (or --skip-duplicate-safe) publish logs 🔐 Trusted publishing token issued for salesforce.<extension> / ...apex-lsp-vscode-extension
  • Confirm vscode-manual-publish.yml's new validation fires for source-run-id + registries: all

@W-24666922@

…- W-24666922

openvsx-publish-release-vsix.yml and vscode-promote-prerelease.yml both ran
`ovsx publish` against a PAT (-p $OVSX_PAT / $IDEE_OVSX_PAT). Switches both
to `ovsx publish --trusted-publishing`, which exchanges this job's GitHub
Actions OIDC ID token for a short-lived publish token instead -- requires
`id-token: write`, which both workflows now grant.

Open VSX's trusted-publisher config binds one identity per extension to the
exact workflow file that requests the OIDC token (its job_workflow_ref).
vscode-promote-prerelease.yml's `ovsx` leg previously ran its own `ovsx
publish` inline, inside the same matrix job as `vsce` -- a different file
from openvsx-publish-release-vsix.yml, which the stable release path uses.
That would mean two different job_workflow_ref values for the same
extension's Open VSX publishes, which a single trusted-publisher entry
can't match.

Split the `ovsx` leg out of that matrix job (GitHub Actions won't let a
`uses:`-based job share a matrix with per-leg different targets) into its
own `publish-ovsx` job that delegates to openvsx-publish-release-vsix.yml
via `uses:`, instead of duplicating the ovsx-publish logic. Every Open VSX
publish for a given extension -- stable, stable hotfix, prerelease,
prerelease/emergency hotfix -- now requests its OIDC token from that one
file. openvsx-publish-release-vsix.yml gained `vsix-name-pattern` and
`exclude-web-vsix` inputs (both optional, default-compatible) so the
delegated call can reproduce vscode-promote-prerelease.yml's existing
filtering for monorepo/multi-extension callers.

vsce/Marketplace publishing (VSCE_PERSONAL_ACCESS_TOKEN) is untouched.
Open VSX's trusted-publisher check matches the OIDC token's workflow_ref
claim (the top-level/triggering workflow), not job_workflow_ref (the
reusable workflow actually executing a job) -- confirmed against
eclipse-openvsx/openvsx's TrustedPublishingService/
GitHubTrustedPublishingProviderSupport source. Consolidating this
reusable workflow's ovsx leg onto openvsx-publish-release-vsix.yml never
closed that gap: salesforcedx-vscode's stable and prerelease flows are
started by two different top-level workflow files regardless of what
runs underneath them, and Open VSX only allows one trusted-publisher
registration per extension (a hard server-side check).

The real fix has to live in salesforcedx-vscode's own top-level
workflows instead, so this reusable workflow goes back to vsce-only.
Keeps the openvsx-publish-release-vsix.yml PAT -> --trusted-publishing
migration, which the new salesforcedx-vscode-side design still delegates
to.
…s - W-24666922

Folds the removal work from the now-closed #200 into this PR, since both
are the same migration and coordinating one github-workflows branch against
each calling repo's own migration PR is simpler than two.

Removes the `ovsx` matrix leg from vscode-promote-prerelease.yml and
vscode-promote-stable.yml entirely (no toggle). Each of these reusable
workflows' real callers (forcedotcom/salesforcedx-vscode,
forcedotcom/apex-language-support) are migrating their Open VSX publishing
onto a dedicated top-level workflow (publishOpenVSXTrusted.yml) that
delegates to this PR's openvsx-publish-release-vsix.yml instead -- required,
not optional, since Open VSX's trusted-publisher match keys off workflow_ref
(the run's initiating workflow) and allows only one registration per
extension.

Also adds a `release-tag` output to vscode-promote-stable.yml (the stable
tag it just created), so a caller can dispatch its own Open VSX publish for
that exact tag afterward.
…o the caller - W-24666922

Same migration, applied to the third apex-language-support entry point that
can publish Open VSX (promote-prerelease.yml and promote-stable.yml already
covered). vscode-manual-publish.yml is this repo's only real caller
(confirmed via GitHub code search), so no toggle needed.

The `publish` job's ovsx leg becomes a deliberate no-op step (not a removed
step, and not a removed matrix entry) specifically so the job's own
success/skip status -- and every downstream needs.publish.result ==
'success' check in this file -- stays exactly as it was. The caller is
expected to dispatch its own single top-level Open VSX workflow afterward,
for the version-tag path only: adds a hard error in `prepare` for
source-run-id + ovsx/all, since a branch-CI-run artifact has no GitHub
Release for a tag-based trusted-publishing dispatch to target, and silently
never publishing to Open VSX for that combination would be worse than
failing loudly.
# 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

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.

can we have a README file about this and link to it instead of spamming the same comments on every file

# `gh workflow run`, not `uses:`, so it's a genuinely new, independently initiated run and not
# nested inside this one).
#
# 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?

@mshanemc mshanemc left a comment

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.

approved w/ non-blocking comments

… in README - W-24666922

The same explanatory comment block was duplicated verbatim across
vscode-promote-prerelease.yml, vscode-promote-stable.yml, and
vscode-manual-publish.yml. Move it into a single README section and
replace each with a short pointer.
@madhur310
madhur310 requested a review from a team as a code owner November 5, 2026 18:12
@madhur310
madhur310 merged commit 6f08acb into main Nov 10, 2026
3 checks passed
madhur310 added a commit to forcedotcom/apex-language-support that referenced this pull request Nov 10, 2026
salesforcecli/github-workflows#199 merged; drop the temporary branch pins.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
madhur310 added a commit to forcedotcom/salesforcedx-vscode that referenced this pull request Nov 10, 2026
salesforcecli/github-workflows#199 merged; drop the temporary branch pins.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
madhur310 added a commit to forcedotcom/salesforcedx-vscode that referenced this pull request Nov 10, 2026
…annels - W-24666922 (#8537)

* fix: try merging workflows

* fix(release): point test pin at combined github-workflows branch - W-24666922

PR #200 (vscode-promote-prerelease.yml's ovsx removal) got folded into #199
and closed, so the temporary test pin here needs to point at #199's branch
too, matching publishOpenVSXTrusted.yml's pin.

* fix(ci): gate Open VSX publish on CTC moratorium check - W-24666922

ctc-open and publish were previously two independent top-level workflows
triggered by the same release event, so a moratorium failure had no way to
actually block the publish. Move ctc-open/verify-published/ctcClose* into
publishOpenVSXTrusted.yml so publish can depend on ctc-open via needs:, and
delete the now-empty publishOpenVSX.yml. Both CTC jobs skip nightly tags,
since prereleases were never CTC-gated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(ci): gate CTC on an explicit do-ctc input, not release-tag shape - W-24666922

The nightly-tag check for skipping CTC (!contains(release-tag, '-nightly'))
broke for emergency prereleases: build-github-release.yml's emergencyPrerelease
mode tags its output as a plain vX.Y.Z, identical in shape to a stable hotfix
retry tag. No tag-shape check can tell these apart -- only the caller knows
which channel it's publishing. Replace it with a do-ctc boolean input
(default false); every stable workflow_dispatch (retry/hotfix) now passes
do-ctc=true explicitly, and the release event (always stable) is covered
directly via github.event_name == 'release'. promote-to-prerelease.yml, the
only prerelease caller, needs no change since omitting do-ctc already
defaults to false.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: explain why do-ctc=true is required in manual publish commands - W-24666922

The runbook had the flag with no explanation. Note why it can't be inferred
from the release tag and must be set explicitly for manual stable dispatches.

* fix(ci): switch github-workflows refs back to @main - W-24666922

salesforcecli/github-workflows#199 merged; drop the temporary branch pins.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants