From 204b14d43a708f6bf067768420e67aa9fe2551c0 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Mon, 13 Jul 2026 20:03:34 +0000 Subject: [PATCH 1/2] fix(release): publish GitHub releases for stable versions only --- .github/workflows/release-vscode-ext.yml | 2 +- apps/cli/.releaserc.cjs | 27 ++++++------ apps/create/.releaserc.cjs | 27 ++++++------ apps/mcp/.releaserc.cjs | 27 ++++++------ apps/vscode-ext/.releaserc.cjs | 42 ++++++++----------- packages/esign/.releaserc.cjs | 27 ++++++------ packages/fonts/.releaserc.cjs | 20 +++++---- packages/react/.releaserc.cjs | 27 ++++++------ packages/sdk/.releaserc.cjs | 27 ++++++------ packages/superdoc/.releaserc.cjs | 13 +++--- packages/template-builder/.releaserc.cjs | 27 ++++++------ scripts/__tests__/release-local.test.mjs | 22 ++++++---- .../semantic-release/linear-commit-sync.cjs | 7 ++-- .../linear-commit-sync.test.cjs | 18 +++++++- 14 files changed, 158 insertions(+), 155 deletions(-) diff --git a/.github/workflows/release-vscode-ext.yml b/.github/workflows/release-vscode-ext.yml index 5abf46d8e7..68c8544825 100644 --- a/.github/workflows/release-vscode-ext.yml +++ b/.github/workflows/release-vscode-ext.yml @@ -2,7 +2,7 @@ # Stable releases are orchestrated centrally by release-stable.yml so that # every stable release shares one concurrency slot and one git push lane. # Note: VS Code Marketplace doesn't support semver prerelease versions, so -# main pushes only build a .vsix and attach it to the GitHub release. +# main pushes create prerelease tags without publishing a .vsix. name: 📦 Release vscode-ext on: diff --git a/apps/cli/.releaserc.cjs b/apps/cli/.releaserc.cjs index 067e8e7596..5753e38d02 100644 --- a/apps/cli/.releaserc.cjs +++ b/apps/cli/.releaserc.cjs @@ -33,12 +33,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; // Use AI-powered notes for stable releases, conventional generator for prereleases @@ -89,13 +87,14 @@ config.plugins.push([ }, ]); -config.plugins.push([ - '@semantic-release/github', - { - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **superdoc-cli** v${nextRelease.version}\n\nThe release is available on [GitHub release](${releases.find(release => release.pluginName === "@semantic-release/github").url})', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **superdoc-cli** v${nextRelease.version}\n\nThe release is available on [GitHub release](${releases.find(release => release.pluginName === "@semantic-release/github").url})', + }, + ]); +} module.exports = config; diff --git a/apps/create/.releaserc.cjs b/apps/create/.releaserc.cjs index d9078f8021..929b868ed6 100644 --- a/apps/create/.releaserc.cjs +++ b/apps/create/.releaserc.cjs @@ -13,12 +13,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; const notesPlugin = isPrerelease ? createReleaseNotesGenerator() : ['semantic-release-ai-notes', { style: 'concise' }]; @@ -49,13 +47,14 @@ config.plugins.push([ }, ]); -config.plugins.push([ - '@semantic-release/github', - { - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/create** v${nextRelease.version}\n\nThe release is available on [GitHub release](${releases.find(release => release.pluginName === "@semantic-release/github").url})', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/create** v${nextRelease.version}\n\nThe release is available on [GitHub release](${releases.find(release => release.pluginName === "@semantic-release/github").url})', + }, + ]); +} module.exports = config; diff --git a/apps/mcp/.releaserc.cjs b/apps/mcp/.releaserc.cjs index 6eff113f91..d9e01c4ce0 100644 --- a/apps/mcp/.releaserc.cjs +++ b/apps/mcp/.releaserc.cjs @@ -37,12 +37,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; // Use AI-powered notes for stable releases, conventional generator for prereleases @@ -92,13 +90,14 @@ config.plugins.push([ }, ]); -config.plugins.push([ - '@semantic-release/github', - { - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/mcp** v${nextRelease.version}\n\nThe release is available on [GitHub release](${releases.find(release => release.pluginName === "@semantic-release/github").url})', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/mcp** v${nextRelease.version}\n\nThe release is available on [GitHub release](${releases.find(release => release.pluginName === "@semantic-release/github").url})', + }, + ]); +} module.exports = config; diff --git a/apps/vscode-ext/.releaserc.cjs b/apps/vscode-ext/.releaserc.cjs index 503eb1649c..5146a6870e 100644 --- a/apps/vscode-ext/.releaserc.cjs +++ b/apps/vscode-ext/.releaserc.cjs @@ -33,12 +33,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags still proceed on main. +const shouldPublishGitHubRelease = !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; // Use AI-powered notes for stable releases, conventional generator for prereleases @@ -65,16 +63,9 @@ const config = { ], }; -// VS Code Marketplace doesn't support semver prerelease versions (e.g., 0.0.1-next.1) -// Only publish stable releases to marketplace; prereleases get GitHub release with .vsix attached -if (isPrerelease) { - config.plugins.push([ - '@semantic-release/exec', - { - prepareCmd: 'pnpm run package', // Creates .vsix file only - }, - ]); -} else { +// VS Code Marketplace doesn't support semver prerelease versions, so only +// stable releases build and publish a .vsix. +if (!isPrerelease) { config.plugins.push([ '@semantic-release/exec', { @@ -103,14 +94,15 @@ config.plugins.push([ }, ]); -config.plugins.push([ - '@semantic-release/github', - { - assets: [{ path: '*.vsix', label: 'VS Code Extension' }], - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **vscode-ext** v${nextRelease.version}', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + assets: [{ path: '*.vsix', label: 'VS Code Extension' }], + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **vscode-ext** v${nextRelease.version}', + }, + ]); +} module.exports = config; diff --git a/packages/esign/.releaserc.cjs b/packages/esign/.releaserc.cjs index 99641a0d19..c5c4580ba7 100644 --- a/packages/esign/.releaserc.cjs +++ b/packages/esign/.releaserc.cjs @@ -21,12 +21,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; // Use AI-powered notes for stable releases, conventional generator for prereleases @@ -64,13 +62,14 @@ config.plugins.push([ }, ]); -config.plugins.push([ - '@semantic-release/github', - { - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **esign** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **esign** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', + }, + ]); +} module.exports = config; diff --git a/packages/fonts/.releaserc.cjs b/packages/fonts/.releaserc.cjs index 88ec66b9b3..d38d7919e9 100644 --- a/packages/fonts/.releaserc.cjs +++ b/packages/fonts/.releaserc.cjs @@ -14,7 +14,8 @@ const branches = [ ]; const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -const shouldCommentOnRelease = !isPrerelease; +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isPrerelease; const shouldCommentOnLinearRelease = true; const notesPlugin = isPrerelease ? createReleaseNotesGenerator() : ['semantic-release-ai-notes', { style: 'concise' }]; @@ -49,13 +50,14 @@ config.plugins.push([ }, ]); -config.plugins.push([ - '@semantic-release/github', - { - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/fonts** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/fonts** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', + }, + ]); +} module.exports = config; diff --git a/packages/react/.releaserc.cjs b/packages/react/.releaserc.cjs index 0914f533f1..ccf5e9b725 100644 --- a/packages/react/.releaserc.cjs +++ b/packages/react/.releaserc.cjs @@ -33,12 +33,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; // Use AI-powered notes for stable releases, conventional generator for prereleases @@ -82,13 +80,14 @@ config.plugins.push([ { teamKeys: ['SD'], addComment: shouldCommentOnLinearRelease, packageName: 'react' }, ]); -config.plugins.push([ - '@semantic-release/github', - { - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/react** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/react** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', + }, + ]); +} module.exports = config; diff --git a/packages/sdk/.releaserc.cjs b/packages/sdk/.releaserc.cjs index d051242492..8fa0b6925a 100644 --- a/packages/sdk/.releaserc.cjs +++ b/packages/sdk/.releaserc.cjs @@ -33,12 +33,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; // Use AI-powered notes for stable releases, conventional generator for prereleases @@ -116,13 +114,14 @@ config.plugins.push([ }, ]); -config.plugins.push([ - '@semantic-release/github', - { - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **superdoc-sdk** v${nextRelease.version}', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **superdoc-sdk** v${nextRelease.version}', + }, + ]); +} module.exports = config; diff --git a/packages/superdoc/.releaserc.cjs b/packages/superdoc/.releaserc.cjs index b80d38b795..aa0f1b67aa 100644 --- a/packages/superdoc/.releaserc.cjs +++ b/packages/superdoc/.releaserc.cjs @@ -61,12 +61,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isLocalPreview && !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; // Use AI-powered notes for stable releases, conventional generator for prereleases @@ -120,13 +118,12 @@ if (!isLocalPreview) { } // GitHub plugin comes last -if (!isLocalPreview) { +if (shouldPublishGitHubRelease) { config.plugins.push([ '@semantic-release/github', { successComment: ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **superdoc** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', - successCommentCondition: shouldCommentOnRelease ? undefined : false, }, ]); } diff --git a/packages/template-builder/.releaserc.cjs b/packages/template-builder/.releaserc.cjs index e1d61522a5..69e93e187a 100644 --- a/packages/template-builder/.releaserc.cjs +++ b/packages/template-builder/.releaserc.cjs @@ -22,12 +22,10 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); -// stable -> main syncs (real merges) re-attribute prereleases to PRs already shipped on @latest. -// Gate per-PR/issue success comments off on prereleases to avoid duplicate "shipped" comments. -const shouldCommentOnRelease = !isPrerelease; -// Linear release comments are the shipped-version breadcrumb inside Linear -// itself, so keep them on for prereleases even while GitHub PR comments stay -// gated separately. +// GitHub Releases are stable-only; prerelease tags and package publishing still proceed. +const shouldPublishGitHubRelease = !isPrerelease; +// Linear release comments remain the shipped-version breadcrumb, so +// prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; // Use AI-powered notes for stable releases, conventional generator for prereleases @@ -65,13 +63,14 @@ config.plugins.push([ }, ]); -config.plugins.push([ - '@semantic-release/github', - { - successComment: - ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **template-builder** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', - successCommentCondition: shouldCommentOnRelease ? undefined : false, - }, -]); +if (shouldPublishGitHubRelease) { + config.plugins.push([ + '@semantic-release/github', + { + successComment: + ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **template-builder** v${nextRelease.version}\n\nThe release is available on [GitHub release](https://github.com/superdoc-dev/superdoc/releases/tag/${nextRelease.gitTag})', + }, + ]); +} module.exports = config; diff --git a/scripts/__tests__/release-local.test.mjs b/scripts/__tests__/release-local.test.mjs index bd4ab3b30d..85c55c07af 100644 --- a/scripts/__tests__/release-local.test.mjs +++ b/scripts/__tests__/release-local.test.mjs @@ -540,7 +540,7 @@ test('stable recovery tracks PyPI gaps when SDK PyPI publishing is enabled', asy ); }); -test('release configs keep GitHub prerelease comments gated while Linear uses the dedicated release-comment policy', async () => { +test('release configs publish GitHub releases only for stable versions while Linear keeps prerelease breadcrumbs', async () => { const releasercPaths = [ 'packages/superdoc/.releaserc.cjs', 'packages/fonts/.releaserc.cjs', @@ -568,15 +568,19 @@ test('release configs keep GitHub prerelease comments gated while Linear uses th `${releasercPath}: must use the commit-message Linear sync plugin, not the PR-branch-based external plugin`, ); - assert.ok( - content.includes('const shouldCommentOnRelease = !isPrerelease'), - `${releasercPath}: must define shouldCommentOnRelease = !isPrerelease so the prerelease comment gate is consistent across configs`, - ); - if (usesGithubPlugin) { assert.ok( - content.includes('successCommentCondition: shouldCommentOnRelease ? undefined : false'), - `${releasercPath}: @semantic-release/github must gate successCommentCondition through shouldCommentOnRelease so prereleases don't re-comment on every PR after a stable -> main sync`, + content.includes('const shouldPublishGitHubRelease =') && content.includes('!isPrerelease'), + `${releasercPath}: must make GitHub release publication stable-only`, + ); + assert.ok( + content.includes('if (shouldPublishGitHubRelease)'), + `${releasercPath}: must gate @semantic-release/github behind shouldPublishGitHubRelease`, + ); + assert.equal( + content.includes('successCommentCondition:'), + false, + `${releasercPath}: prereleases must omit the GitHub plugin instead of configuring its success hook`, ); } @@ -587,7 +591,7 @@ test('release configs keep GitHub prerelease comments gated while Linear uses th ); assert.ok( content.includes('addComment: shouldCommentOnLinearRelease'), - `${releasercPath}: Linear addComment must use the dedicated Linear comment gate so prerelease Linear breadcrumbs stay on while GitHub PR comments remain gated`, + `${releasercPath}: Linear addComment must use the dedicated Linear comment gate so prerelease breadcrumbs remain available without GitHub Releases`, ); assert.equal( content.includes('addComment: true'), diff --git a/scripts/semantic-release/linear-commit-sync.cjs b/scripts/semantic-release/linear-commit-sync.cjs index 501101373e..efb5b86322 100644 --- a/scripts/semantic-release/linear-commit-sync.cjs +++ b/scripts/semantic-release/linear-commit-sync.cjs @@ -266,7 +266,7 @@ function getLabelColor(releaseType) { return colors[releaseType] || '#4752C4'; } -function buildReleaseUrl(repositoryUrl, gitTag) { +function buildReleaseUrl(repositoryUrl, gitTag, channel) { if (!repositoryUrl || !gitTag) { return ''; } @@ -274,13 +274,14 @@ function buildReleaseUrl(repositoryUrl, gitTag) { if (!match) { return ''; } - return `https://github.com/${match[1]}/${match[2]}/releases/tag/${gitTag}`; + const tagPath = channel === 'next' ? 'tree' : 'releases/tag'; + return `https://github.com/${match[1]}/${match[2]}/${tagPath}/${gitTag}`; } function formatComment(template, version, channel, packageName, gitTag, repositoryUrl) { const channelText = channel ? `(${channel} channel)` : ''; const packageText = packageName ? `**${packageName}**` : ''; - const releaseUrl = buildReleaseUrl(repositoryUrl, gitTag); + const releaseUrl = buildReleaseUrl(repositoryUrl, gitTag, channel); const releaseLink = releaseUrl ? `[${version}](${releaseUrl})` : version; const tpl = template || 'Released in {package} v{releaseLink} {channel}'; return tpl diff --git a/scripts/semantic-release/linear-commit-sync.test.cjs b/scripts/semantic-release/linear-commit-sync.test.cjs index 02a17116d1..9cf8c28daa 100644 --- a/scripts/semantic-release/linear-commit-sync.test.cjs +++ b/scripts/semantic-release/linear-commit-sync.test.cjs @@ -89,7 +89,7 @@ test('collectIssueIdsFromCommits ignores generated release commits with old note }); -test('formatComment keeps the existing release comment template behavior', () => { +test('formatComment links prerelease comments to the Git tag', () => { assert.equal( formatComment( 'shipped in {package} {releaseLink} {channel}', @@ -99,7 +99,21 @@ test('formatComment keeps the existing release comment template behavior', () => 'v1.2.3', 'https://github.com/superdoc-dev/superdoc.git', ), - 'shipped in **superdoc** [1.2.3](https://github.com/superdoc-dev/superdoc/releases/tag/v1.2.3) (next channel)', + 'shipped in **superdoc** [1.2.3](https://github.com/superdoc-dev/superdoc/tree/v1.2.3) (next channel)', + ); +}); + +test('formatComment links stable comments to the GitHub release', () => { + assert.equal( + formatComment( + 'shipped in {package} {releaseLink} {channel}', + '1.2.3', + 'latest', + 'superdoc', + 'v1.2.3', + 'https://github.com/superdoc-dev/superdoc.git', + ), + 'shipped in **superdoc** [1.2.3](https://github.com/superdoc-dev/superdoc/releases/tag/v1.2.3) (latest channel)', ); }); From b6ba3b39e49e8722bcd5246ccb5c2c315a1bfc87 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Mon, 13 Jul 2026 20:21:44 +0000 Subject: [PATCH 2/2] fix(release): fail closed when release branch is unknown --- apps/cli/.releaserc.cjs | 2 +- apps/create/.releaserc.cjs | 2 +- apps/mcp/.releaserc.cjs | 2 +- apps/vscode-ext/.releaserc.cjs | 2 +- packages/esign/.releaserc.cjs | 2 +- packages/fonts/.releaserc.cjs | 2 +- packages/react/.releaserc.cjs | 2 +- packages/sdk/.releaserc.cjs | 2 +- packages/superdoc/.releaserc.cjs | 2 +- packages/template-builder/.releaserc.cjs | 2 +- scripts/__tests__/release-local.test.mjs | 6 ++++-- 11 files changed, 14 insertions(+), 12 deletions(-) diff --git a/apps/cli/.releaserc.cjs b/apps/cli/.releaserc.cjs index 5753e38d02..9bc6e7e43a 100644 --- a/apps/cli/.releaserc.cjs +++ b/apps/cli/.releaserc.cjs @@ -34,7 +34,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/apps/create/.releaserc.cjs b/apps/create/.releaserc.cjs index 929b868ed6..67afe4662a 100644 --- a/apps/create/.releaserc.cjs +++ b/apps/create/.releaserc.cjs @@ -14,7 +14,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/apps/mcp/.releaserc.cjs b/apps/mcp/.releaserc.cjs index d9e01c4ce0..d61637a370 100644 --- a/apps/mcp/.releaserc.cjs +++ b/apps/mcp/.releaserc.cjs @@ -38,7 +38,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/apps/vscode-ext/.releaserc.cjs b/apps/vscode-ext/.releaserc.cjs index 5146a6870e..05b61d559f 100644 --- a/apps/vscode-ext/.releaserc.cjs +++ b/apps/vscode-ext/.releaserc.cjs @@ -34,7 +34,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags still proceed on main. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/packages/esign/.releaserc.cjs b/packages/esign/.releaserc.cjs index c5c4580ba7..2ab6f02d36 100644 --- a/packages/esign/.releaserc.cjs +++ b/packages/esign/.releaserc.cjs @@ -22,7 +22,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/packages/fonts/.releaserc.cjs b/packages/fonts/.releaserc.cjs index d38d7919e9..1d0d5b9fba 100644 --- a/packages/fonts/.releaserc.cjs +++ b/packages/fonts/.releaserc.cjs @@ -15,7 +15,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; const shouldCommentOnLinearRelease = true; const notesPlugin = isPrerelease ? createReleaseNotesGenerator() : ['semantic-release-ai-notes', { style: 'concise' }]; diff --git a/packages/react/.releaserc.cjs b/packages/react/.releaserc.cjs index ccf5e9b725..c9f4d8a4a1 100644 --- a/packages/react/.releaserc.cjs +++ b/packages/react/.releaserc.cjs @@ -34,7 +34,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/packages/sdk/.releaserc.cjs b/packages/sdk/.releaserc.cjs index 8fa0b6925a..9533b9fae6 100644 --- a/packages/sdk/.releaserc.cjs +++ b/packages/sdk/.releaserc.cjs @@ -34,7 +34,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/packages/superdoc/.releaserc.cjs b/packages/superdoc/.releaserc.cjs index aa0f1b67aa..a99b91086c 100644 --- a/packages/superdoc/.releaserc.cjs +++ b/packages/superdoc/.releaserc.cjs @@ -62,7 +62,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isLocalPreview && !isPrerelease; +const shouldPublishGitHubRelease = !isLocalPreview && Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/packages/template-builder/.releaserc.cjs b/packages/template-builder/.releaserc.cjs index 69e93e187a..1230f8c674 100644 --- a/packages/template-builder/.releaserc.cjs +++ b/packages/template-builder/.releaserc.cjs @@ -23,7 +23,7 @@ const branches = [ const isPrerelease = branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease); // GitHub Releases are stable-only; prerelease tags and package publishing still proceed. -const shouldPublishGitHubRelease = !isPrerelease; +const shouldPublishGitHubRelease = Boolean(branch) && !isPrerelease; // Linear release comments remain the shipped-version breadcrumb, so // prereleases link to their Git tags when no GitHub Release exists. const shouldCommentOnLinearRelease = true; diff --git a/scripts/__tests__/release-local.test.mjs b/scripts/__tests__/release-local.test.mjs index 85c55c07af..36f356b5a7 100644 --- a/scripts/__tests__/release-local.test.mjs +++ b/scripts/__tests__/release-local.test.mjs @@ -570,8 +570,10 @@ test('release configs publish GitHub releases only for stable versions while Lin if (usesGithubPlugin) { assert.ok( - content.includes('const shouldPublishGitHubRelease =') && content.includes('!isPrerelease'), - `${releasercPath}: must make GitHub release publication stable-only`, + content.includes('const shouldPublishGitHubRelease =') && + content.includes('Boolean(branch)') && + content.includes('!isPrerelease'), + `${releasercPath}: must fail closed when the release branch is unknown and publish GitHub releases only for stable versions`, ); assert.ok( content.includes('if (shouldPublishGitHubRelease)'),