Skip to content

Commit 4809517

Browse files
committed
fix(release): preview local dry runs on inferred release branches
1 parent cbf2b9f commit 4809517

4 files changed

Lines changed: 481 additions & 52 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"lint:jsdoc:fix": "eslint packages/super-editor/src/editors/v1/extensions/field-annotation/field-annotation.js --config eslint.config.docs.mjs --fix",
5252
"pack": "pnpm run build:superdoc && pnpm run type-check && pnpm --prefix ./packages/superdoc run pack",
5353
"release": "pnpm run build:superdoc && pnpm run type-check && pnpm --prefix packages/superdoc exec semantic-release",
54-
"release:dry-run": "pnpm run build:superdoc && pnpm run type-check && pnpm --prefix packages/superdoc exec semantic-release --dry-run",
54+
"release:dry-run": "pnpm run build:superdoc && pnpm run type-check && node scripts/release-local-superdoc.mjs --dry-run",
5555
"release:local": "pnpm run build:superdoc && pnpm run type-check && node scripts/release-local-stable.mjs",
5656
"release:local:superdoc": "pnpm run build:superdoc && pnpm run type-check && node scripts/release-local-superdoc.mjs",
5757
"release:local:cli": "pnpm run build:superdoc && pnpm run type-check && node scripts/release-local-cli.mjs",

packages/superdoc/.releaserc.cjs

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Object.keys(require.cache)
3939
})
4040

4141
const branch = process.env.GITHUB_REF_NAME || process.env.CI_COMMIT_BRANCH
42+
const isLocalPreview = process.env.SUPERDOC_RELEASE_PREVIEW === '1'
4243

4344
const branches = [
4445
{
@@ -62,16 +63,18 @@ const isPrerelease = branches.some(
6263
)
6364

6465
// Use AI-powered notes for stable releases, conventional generator for prereleases
65-
const notesPlugin = isPrerelease
66+
const notesPlugin = isLocalPreview || isPrerelease
6667
? '@semantic-release/release-notes-generator'
6768
: ['semantic-release-ai-notes', { style: 'concise' }]
6869

6970
const config = {
7071
branches,
7172
tagFormat: 'v${version}',
72-
plugins: [
73-
'@semantic-release/commit-analyzer',
74-
notesPlugin,
73+
plugins: ['@semantic-release/commit-analyzer', notesPlugin],
74+
}
75+
76+
if (!isLocalPreview) {
77+
config.plugins.push(
7578
// NPM plugin MUST come before git plugin
7679
[
7780
'semantic-release-pnpm',
@@ -80,12 +83,12 @@ const config = {
8083
}
8184
],
8285
'../../scripts/publish-superdoc.cjs'
83-
],
86+
)
8487
}
8588

86-
// Only add changelog and git plugins for non-prerelease branches
89+
// Only add changelog and git plugins for non-prerelease, non-preview branches
8790

88-
if (!isPrerelease) {
91+
if (!isLocalPreview && !isPrerelease) {
8992
// Git plugin commits the version bump back to the branch.
9093
// No changelog — release notes live on the GitHub release only.
9194
config.plugins.push([
@@ -99,19 +102,23 @@ if (!isPrerelease) {
99102
}
100103

101104
// Linear integration - labels issues with version on release
102-
config.plugins.push(['semantic-release-linear-app', {
103-
teamKeys: ['SD'],
104-
addComment: true,
105-
packageName: 'superdoc',
106-
commentTemplate: 'shipped in {package} {releaseLink} {channel}'
107-
}])
105+
if (!isLocalPreview) {
106+
config.plugins.push(['semantic-release-linear-app', {
107+
teamKeys: ['SD'],
108+
addComment: true,
109+
packageName: 'superdoc',
110+
commentTemplate: 'shipped in {package} {releaseLink} {channel}'
111+
}])
112+
}
108113

109114
// GitHub plugin comes last
110-
config.plugins.push([
111-
'@semantic-release/github',
112-
{
113-
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})',
114-
}
115-
])
115+
if (!isLocalPreview) {
116+
config.plugins.push([
117+
'@semantic-release/github',
118+
{
119+
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})',
120+
}
121+
])
122+
}
116123

117124
module.exports = config

scripts/__tests__/release-local.test.mjs

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ import { readFile } from 'node:fs/promises';
33
import path from 'node:path';
44
import test from 'node:test';
55
import { fileURLToPath } from 'node:url';
6-
import { inferDryRunWouldRelease } from '../release-local.mjs';
6+
import {
7+
buildSemanticReleaseArgs,
8+
buildSemanticReleaseEnv,
9+
detectPreviewTargetFromBranchName,
10+
getRepositoryUrlCandidates,
11+
inferPreviewTargetBranch,
12+
inferDryRunWouldRelease,
13+
splitPreviewArgs,
14+
} from '../release-local.mjs';
715

816
const __dirname = path.dirname(fileURLToPath(import.meta.url));
917
const REPO_ROOT = path.resolve(__dirname, '../../');
@@ -31,6 +39,93 @@ test('inferDryRunWouldRelease detects pending release previews', () => {
3139
);
3240
});
3341

42+
test('release-local helper does not inject semantic-release branch overrides', () => {
43+
assert.deepEqual(
44+
buildSemanticReleaseArgs({
45+
packageCwd: 'packages/superdoc',
46+
extraArgs: ['--dry-run'],
47+
}),
48+
[
49+
'--prefix',
50+
'packages/superdoc',
51+
'exec',
52+
'semantic-release',
53+
'--no-ci',
54+
'--dry-run',
55+
],
56+
);
57+
});
58+
59+
test('release-local helper strips custom preview-branch flags before forwarding args', () => {
60+
assert.deepEqual(
61+
splitPreviewArgs(['--dry-run', '--preview-branch', 'stable', '--debug']),
62+
{
63+
semanticReleaseArgs: ['--dry-run', '--debug'],
64+
previewBranchOverride: 'stable',
65+
},
66+
);
67+
});
68+
69+
test('release-local helper supports equals-style preview branch overrides', () => {
70+
assert.deepEqual(
71+
splitPreviewArgs(['--dry-run', '--preview-branch=main']),
72+
{
73+
semanticReleaseArgs: ['--dry-run'],
74+
previewBranchOverride: 'main',
75+
},
76+
);
77+
});
78+
79+
test('release-local helper marks dry runs as local preview mode', () => {
80+
const env = buildSemanticReleaseEnv({
81+
branch: 'stable',
82+
extraArgs: ['--dry-run'],
83+
baseEnv: {},
84+
});
85+
86+
assert.equal(env.GITHUB_REF_NAME, 'stable');
87+
assert.equal(env.SUPERDOC_RELEASE_PREVIEW, '1');
88+
assert.equal(env.LEFTHOOK, '0');
89+
});
90+
91+
test('release-local helper infers preview target from merge-branch names', () => {
92+
assert.equal(
93+
detectPreviewTargetFromBranchName('merge/main-into-stable-2026-04-24', ['stable', 'main']),
94+
'stable',
95+
);
96+
assert.equal(
97+
detectPreviewTargetFromBranchName('hotfix/to-0.29.x-urgent', ['stable', 'main', '0.29.x']),
98+
'0.29.x',
99+
);
100+
});
101+
102+
test('release-local helper honors explicit preview-branch overrides', () => {
103+
assert.equal(
104+
inferPreviewTargetBranch({
105+
currentBranch: 'merge/main-into-stable-2026-04-24',
106+
releaseBranches: ['stable', 'main'],
107+
previewBranchOverride: 'main',
108+
}),
109+
'main',
110+
);
111+
});
112+
113+
test('release-local helper rewrites both ssh and https repository urls for previews', () => {
114+
const candidates = getRepositoryUrlCandidates('packages/superdoc');
115+
assert.ok(
116+
candidates.includes('git+https://github.com/superdoc-dev/superdoc.git'),
117+
'packages/superdoc/package.json repository url must be included',
118+
);
119+
assert.ok(
120+
candidates.includes('https://github.com/superdoc-dev/superdoc.git'),
121+
'git+https package repository urls must normalize to https for git rewrites',
122+
);
123+
assert.ok(
124+
candidates.includes('git@github.com:superdoc-dev/superdoc.git'),
125+
'origin ssh urls must also be rewritten for preview remotes',
126+
);
127+
});
128+
34129
test('release-local helper prunes local-only tags across all release namespaces', async () => {
35130
const content = await readRepoFile('scripts/release-local.mjs');
36131
assert.ok(
@@ -46,6 +141,34 @@ test('release-local helper prunes local-only tags across all release namespaces'
46141
content.includes("'v[0-9]*'"),
47142
'scripts/release-local.mjs: superdoc tag matching must not also match vscode release tags',
48143
);
144+
assert.ok(
145+
content.includes('pruneLocalOnlyReleaseTags({ allowRemoteFailure: isDryRunEnabled(semanticReleaseArgs) })'),
146+
'scripts/release-local.mjs: dry-run previews must treat remote tag pruning as best-effort',
147+
);
148+
});
149+
150+
test('root release:dry-run script uses the local preview helper', async () => {
151+
const content = await readRepoFile('package.json');
152+
assert.ok(
153+
content.includes('"release:dry-run": "pnpm run build:superdoc && pnpm run type-check && node scripts/release-local-superdoc.mjs --dry-run"'),
154+
'package.json: release:dry-run must delegate to the local preview helper',
155+
);
156+
});
157+
158+
test('superdoc releaserc uses preview mode to avoid AI notes and side-effect plugins', async () => {
159+
const content = await readRepoFile('packages/superdoc/.releaserc.cjs');
160+
assert.ok(
161+
content.includes("const isLocalPreview = process.env.SUPERDOC_RELEASE_PREVIEW === '1'"),
162+
'packages/superdoc/.releaserc.cjs: must detect local preview mode',
163+
);
164+
assert.ok(
165+
content.includes("const notesPlugin = isLocalPreview || isPrerelease"),
166+
'packages/superdoc/.releaserc.cjs: preview mode must fall back to conventional release notes',
167+
);
168+
assert.ok(
169+
content.includes('if (!isLocalPreview) {'),
170+
'packages/superdoc/.releaserc.cjs: preview mode must gate side-effect plugins',
171+
);
49172
});
50173

51174
test('stable orchestrator prunes before snapshot and reports would-release previews', async () => {

0 commit comments

Comments
 (0)