From 07f9f902e6fa93dd29554e28d5003e3437578684 Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Fri, 18 Sep 2026 15:45:07 -0400 Subject: [PATCH 1/2] fix(release): skip VS Code extension publish when VSCE_PAT/OVSX_PAT are unset vsce publish was called with --pat '' because the VSCE_PAT/OVSX_PAT secrets aren't configured yet, which made Azure DevOps reject the request and aborted the whole semantic-release run before it could create the GitHub Release for the already-published npm packages. Skip the marketplace/registry publish with a warning when either PAT is missing instead of throwing, so this optional integration can't block the rest of the release. Closes #49 --- scripts/release-vscode-extension.mjs | 54 ++++++++++++++++++---------- scripts/release.test.mjs | 13 +++++++ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/scripts/release-vscode-extension.mjs b/scripts/release-vscode-extension.mjs index f26cf55..61a890a 100644 --- a/scripts/release-vscode-extension.mjs +++ b/scripts/release-vscode-extension.mjs @@ -18,8 +18,16 @@ export function setVersion(version) { // Build, package, and publish the extension to both the VS Code Marketplace // and Open VSX (the registry Cursor and other VS Code-compatible editors -// use). Requires VSCE_PAT / OVSX_PAT in the environment. +// use). VSCE_PAT / OVSX_PAT are optional: until they're configured as repo +// secrets, publishing to that registry is skipped with a warning rather than +// failing the whole semantic-release run (which would otherwise also block +// the already-published npm packages from getting a GitHub Release). export function publish() { + if (!process.env.VSCE_PAT && !process.env.OVSX_PAT) { + console.warn('VSCE_PAT and OVSX_PAT are not set; skipping VS Code extension publish.'); + return; + } + execFileSync('pnpm', ['--filter', 'hdrify-vscode-extension', 'run', 'build'], { stdio: 'inherit' }); execFileSync('pnpm', ['--filter', 'hdrify-vscode-extension', 'exec', 'vsce', 'package', '--no-dependencies'], { stdio: 'inherit', @@ -28,22 +36,30 @@ export function publish() { extensionPath, `hdrify-vscode-extension-${JSON.parse(readFileSync(resolve(extensionPath, 'package.json'), 'utf8')).version}.vsix`, ); - execFileSync( - 'pnpm', - [ - '--filter', - 'hdrify-vscode-extension', - 'exec', - 'vsce', - 'publish', - '--packagePath', - vsix, - '--pat', - process.env.VSCE_PAT, - ], - { - stdio: 'inherit', - }, - ); - execFileSync('npx', ['ovsx', 'publish', vsix, '--pat', process.env.OVSX_PAT], { stdio: 'inherit' }); + + if (process.env.VSCE_PAT) { + execFileSync( + 'pnpm', + [ + '--filter', + 'hdrify-vscode-extension', + 'exec', + 'vsce', + 'publish', + '--packagePath', + vsix, + '--pat', + process.env.VSCE_PAT, + ], + { stdio: 'inherit' }, + ); + } else { + console.warn('VSCE_PAT is not set; skipping VS Code Marketplace publish.'); + } + + if (process.env.OVSX_PAT) { + execFileSync('npx', ['ovsx', 'publish', vsix, '--pat', process.env.OVSX_PAT], { stdio: 'inherit' }); + } else { + console.warn('OVSX_PAT is not set; skipping Open VSX publish.'); + } } diff --git a/scripts/release.test.mjs b/scripts/release.test.mjs index bf53593..9ce9fab 100644 --- a/scripts/release.test.mjs +++ b/scripts/release.test.mjs @@ -4,6 +4,7 @@ import { resolve } from 'node:path'; import test from 'node:test'; import { analyzeCommits } from '@semantic-release/commit-analyzer'; import { checkPullRequest } from './check-pr.mjs'; +import { publish as publishExtension } from './release-vscode-extension.mjs'; // Published via pnpm publish directly (see release.config.js): pnpm rewrites // workspace:* deps and packs the `files` field natively, so these packages @@ -40,6 +41,18 @@ for (const [message, expected] of [ }); } +test('VS Code extension publish is skipped (not thrown) when VSCE_PAT/OVSX_PAT are unset', () => { + const { VSCE_PAT, OVSX_PAT } = process.env; + delete process.env.VSCE_PAT; + delete process.env.OVSX_PAT; + try { + assert.doesNotThrow(() => publishExtension()); + } finally { + if (VSCE_PAT !== undefined) process.env.VSCE_PAT = VSCE_PAT; + if (OVSX_PAT !== undefined) process.env.OVSX_PAT = OVSX_PAT; + } +}); + test('PR policy enforces issue link and integration target, not branch name', () => { const pr = { base: { ref: 'main' }, From 2a41dc489ceece7314b24d03eeba01aabf91afbd Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Fri, 18 Sep 2026 15:49:37 -0400 Subject: [PATCH 2/2] fix(release): fail fast with a clear error instead of skipping publish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Publishing to the VS Code Marketplace/Open VSX still requires VSCE_PAT/OVSX_PAT — this is not optional. Replace the confusing `vsce publish --pat ''` / TF400813 Azure DevOps error with an explicit error naming the missing secret(s) and pointing at RELEASING.md. --- scripts/release-vscode-extension.mjs | 54 ++++++++++++---------------- scripts/release.test.mjs | 4 +-- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/scripts/release-vscode-extension.mjs b/scripts/release-vscode-extension.mjs index 61a890a..7aba4e1 100644 --- a/scripts/release-vscode-extension.mjs +++ b/scripts/release-vscode-extension.mjs @@ -18,14 +18,13 @@ export function setVersion(version) { // Build, package, and publish the extension to both the VS Code Marketplace // and Open VSX (the registry Cursor and other VS Code-compatible editors -// use). VSCE_PAT / OVSX_PAT are optional: until they're configured as repo -// secrets, publishing to that registry is skipped with a warning rather than -// failing the whole semantic-release run (which would otherwise also block -// the already-published npm packages from getting a GitHub Release). +// use). Requires VSCE_PAT / OVSX_PAT in the environment. export function publish() { - if (!process.env.VSCE_PAT && !process.env.OVSX_PAT) { - console.warn('VSCE_PAT and OVSX_PAT are not set; skipping VS Code extension publish.'); - return; + const missing = [!process.env.VSCE_PAT && 'VSCE_PAT', !process.env.OVSX_PAT && 'OVSX_PAT'].filter(Boolean); + if (missing.length > 0) { + throw new Error( + `Cannot publish the VS Code extension: ${missing.join(' and ')} ${missing.length > 1 ? 'are' : 'is'} not set. Configure ${missing.length > 1 ? 'them' : 'it'} as repository secret(s) (see RELEASING.md) before dispatching a release.`, + ); } execFileSync('pnpm', ['--filter', 'hdrify-vscode-extension', 'run', 'build'], { stdio: 'inherit' }); @@ -37,29 +36,20 @@ export function publish() { `hdrify-vscode-extension-${JSON.parse(readFileSync(resolve(extensionPath, 'package.json'), 'utf8')).version}.vsix`, ); - if (process.env.VSCE_PAT) { - execFileSync( - 'pnpm', - [ - '--filter', - 'hdrify-vscode-extension', - 'exec', - 'vsce', - 'publish', - '--packagePath', - vsix, - '--pat', - process.env.VSCE_PAT, - ], - { stdio: 'inherit' }, - ); - } else { - console.warn('VSCE_PAT is not set; skipping VS Code Marketplace publish.'); - } - - if (process.env.OVSX_PAT) { - execFileSync('npx', ['ovsx', 'publish', vsix, '--pat', process.env.OVSX_PAT], { stdio: 'inherit' }); - } else { - console.warn('OVSX_PAT is not set; skipping Open VSX publish.'); - } + execFileSync( + 'pnpm', + [ + '--filter', + 'hdrify-vscode-extension', + 'exec', + 'vsce', + 'publish', + '--packagePath', + vsix, + '--pat', + process.env.VSCE_PAT, + ], + { stdio: 'inherit' }, + ); + execFileSync('npx', ['ovsx', 'publish', vsix, '--pat', process.env.OVSX_PAT], { stdio: 'inherit' }); } diff --git a/scripts/release.test.mjs b/scripts/release.test.mjs index 9ce9fab..831b012 100644 --- a/scripts/release.test.mjs +++ b/scripts/release.test.mjs @@ -41,12 +41,12 @@ for (const [message, expected] of [ }); } -test('VS Code extension publish is skipped (not thrown) when VSCE_PAT/OVSX_PAT are unset', () => { +test('VS Code extension publish fails fast with a clear error when VSCE_PAT/OVSX_PAT are unset', () => { const { VSCE_PAT, OVSX_PAT } = process.env; delete process.env.VSCE_PAT; delete process.env.OVSX_PAT; try { - assert.doesNotThrow(() => publishExtension()); + assert.throws(() => publishExtension(), /VSCE_PAT and OVSX_PAT are not set/); } finally { if (VSCE_PAT !== undefined) process.env.VSCE_PAT = VSCE_PAT; if (OVSX_PAT !== undefined) process.env.OVSX_PAT = OVSX_PAT;