mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-06-16 15:13:31 -07:00
fix: scope prerelease note reuse by version
This commit is contained in:
@@ -2,3 +2,4 @@ type: fixed
|
|||||||
area: release
|
area: release
|
||||||
|
|
||||||
- Kept the GitHub release `What's Changed` and `New Contributors` attribution sections when CI regenerates release notes from the committed changelog.
|
- Kept the GitHub release `What's Changed` and `New Contributors` attribution sections when CI regenerates release notes from the committed changelog.
|
||||||
|
- Scoped prerelease note reuse to the same base version so a new beta line starts from current fragments instead of stale notes from older prereleases.
|
||||||
|
|||||||
+4
-2
@@ -61,8 +61,10 @@
|
|||||||
committed file — so review it before committing. If you add more
|
committed file — so review it before committing. If you add more
|
||||||
`changes/*.md` fragments for a later beta/RC, rerun
|
`changes/*.md` fragments for a later beta/RC, rerun
|
||||||
`bun run changelog:prerelease-notes --version <version>`; the generator uses
|
`bun run changelog:prerelease-notes --version <version>`; the generator uses
|
||||||
the existing prerelease notes as the baseline and asks Claude to merge only
|
the existing prerelease notes as the baseline only when their hidden
|
||||||
the new fragment material. Do not run `bun run changelog:build`.
|
`prerelease-base-version` marker matches the current base version, and asks
|
||||||
|
Claude to merge only the new fragment material. Do not run
|
||||||
|
`bun run changelog:build`.
|
||||||
6. Tag the commit: `git tag v<version>`.
|
6. Tag the commit: `git tag v<version>`.
|
||||||
7. Push commit + tag.
|
7. Push commit + tag.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.
|
> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.
|
||||||
|
|
||||||
|
<!-- prerelease-base-version: 0.17.0 -->
|
||||||
|
|
||||||
## Highlights
|
## Highlights
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ test('writePrereleaseNotesForVersion writes cumulative beta notes without mutati
|
|||||||
|
|
||||||
const prereleaseNotes = fs.readFileSync(outputPath, 'utf8');
|
const prereleaseNotes = fs.readFileSync(outputPath, 'utf8');
|
||||||
assert.match(prereleaseNotes, /^> This is a prerelease build for testing\./m);
|
assert.match(prereleaseNotes, /^> This is a prerelease build for testing\./m);
|
||||||
|
assert.match(prereleaseNotes, /<!-- prerelease-base-version: 0\.11\.3 -->/);
|
||||||
assert.match(prereleaseNotes, /## Highlights\n### Added\n- Polished: added entry\./);
|
assert.match(prereleaseNotes, /## Highlights\n### Added\n- Polished: added entry\./);
|
||||||
assert.match(prereleaseNotes, /### Fixed\n- Polished: fixed entry\./);
|
assert.match(prereleaseNotes, /### Fixed\n- Polished: fixed entry\./);
|
||||||
assert.match(prereleaseNotes, /## Installation\n\nSee the README and docs\/installation guide/);
|
assert.match(prereleaseNotes, /## Installation\n\nSee the README and docs\/installation guide/);
|
||||||
@@ -620,6 +621,8 @@ test('writePrereleaseNotesForVersion reuses existing prerelease notes when addin
|
|||||||
const existingNotes = [
|
const existingNotes = [
|
||||||
'> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.',
|
'> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.',
|
||||||
'',
|
'',
|
||||||
|
'<!-- prerelease-base-version: 0.11.3 -->',
|
||||||
|
'',
|
||||||
'## Highlights',
|
'## Highlights',
|
||||||
'### Added',
|
'### Added',
|
||||||
'- Overlay: Previous beta entry.',
|
'- Overlay: Previous beta entry.',
|
||||||
@@ -679,6 +682,61 @@ test('writePrereleaseNotesForVersion reuses existing prerelease notes when addin
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('writePrereleaseNotesForVersion ignores unmarked prerelease notes from an older release line', async () => {
|
||||||
|
const { writePrereleaseNotesForVersion } = await loadModule();
|
||||||
|
const workspace = createWorkspace('prerelease-ignore-unmarked-old-notes');
|
||||||
|
const projectRoot = path.join(workspace, 'SubMiner');
|
||||||
|
const existingNotes = [
|
||||||
|
'> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.',
|
||||||
|
'',
|
||||||
|
'## Highlights',
|
||||||
|
'### Added',
|
||||||
|
'- Settings Window: Previous release line entry.',
|
||||||
|
'',
|
||||||
|
'## Installation',
|
||||||
|
'',
|
||||||
|
'See the README and docs/installation guide for full setup steps.',
|
||||||
|
'',
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
fs.mkdirSync(path.join(projectRoot, 'changes'), { recursive: true });
|
||||||
|
fs.mkdirSync(path.join(projectRoot, 'release'), { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(projectRoot, 'package.json'),
|
||||||
|
JSON.stringify({ name: 'subminer', version: '0.17.0-beta.1' }, null, 2),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
fs.writeFileSync(path.join(projectRoot, 'release', 'prerelease-notes.md'), existingNotes, 'utf8');
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(projectRoot, 'changes', '001.md'),
|
||||||
|
[
|
||||||
|
'type: changed',
|
||||||
|
'area: overlay',
|
||||||
|
'',
|
||||||
|
'- Replaced subtitle delay actions with native mpv keybindings.',
|
||||||
|
].join('\n'),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stub = defaultStubClaude();
|
||||||
|
const outputPath = writePrereleaseNotesForVersion({
|
||||||
|
cwd: projectRoot,
|
||||||
|
version: '0.17.0-beta.1',
|
||||||
|
deps: { runClaude: stub.runClaude },
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(stub.calls.length, 1, 'prerelease should issue exactly one Claude call');
|
||||||
|
assert.doesNotMatch(stub.calls[0]!.input, /EXISTING PRERELEASE NOTES/);
|
||||||
|
assert.doesNotMatch(stub.calls[0]!.input, /Settings Window: Previous release line entry/);
|
||||||
|
|
||||||
|
const prereleaseNotes = fs.readFileSync(outputPath, 'utf8');
|
||||||
|
assert.match(prereleaseNotes, /### Changed\n- Polished: changed entry\./);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(workspace, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('writePrereleaseNotesForVersion prompts Claude to revise stale prerelease bullets instead of appending fix churn', async () => {
|
test('writePrereleaseNotesForVersion prompts Claude to revise stale prerelease bullets instead of appending fix churn', async () => {
|
||||||
const { writePrereleaseNotesForVersion } = await loadModule();
|
const { writePrereleaseNotesForVersion } = await loadModule();
|
||||||
const workspace = createWorkspace('prerelease-net-outcome-prompt');
|
const workspace = createWorkspace('prerelease-net-outcome-prompt');
|
||||||
@@ -686,6 +744,8 @@ test('writePrereleaseNotesForVersion prompts Claude to revise stale prerelease b
|
|||||||
const existingNotes = [
|
const existingNotes = [
|
||||||
'> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.',
|
'> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.',
|
||||||
'',
|
'',
|
||||||
|
'<!-- prerelease-base-version: 0.12.0 -->',
|
||||||
|
'',
|
||||||
'## Highlights',
|
'## Highlights',
|
||||||
'### Added',
|
'### Added',
|
||||||
'- Config Window: Previous beta entry.',
|
'- Config Window: Previous beta entry.',
|
||||||
|
|||||||
@@ -93,6 +93,40 @@ function isSupportedPrereleaseVersion(version: string): boolean {
|
|||||||
return /^\d+\.\d+\.\d+-(beta|rc)\.\d+$/u.test(normalizeVersion(version));
|
return /^\d+\.\d+\.\d+-(beta|rc)\.\d+$/u.test(normalizeVersion(version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolvePrereleaseBaseVersion(version: string): string {
|
||||||
|
const match = /^(\d+\.\d+\.\d+)-(?:beta|rc)\.\d+$/u.exec(normalizeVersion(version));
|
||||||
|
if (!match) {
|
||||||
|
throw new Error(
|
||||||
|
`Unsupported prerelease version (${version}). Expected x.y.z-beta.N or x.y.z-rc.N.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return match[1]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPrereleaseBaseVersionMarker(version: string): string {
|
||||||
|
return `<!-- prerelease-base-version: ${resolvePrereleaseBaseVersion(version)} -->`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractPrereleaseBaseVersionMarker(notes: string): string | null {
|
||||||
|
return (
|
||||||
|
/<!--\s*prerelease-base-version:\s*(\d+\.\d+\.\d+)\s*-->/u.exec(notes)?.[1] ?? null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripPrereleaseMetadata(notes: string): string {
|
||||||
|
return notes
|
||||||
|
.replace(/<!--\s*prerelease-base-version:\s*\d+\.\d+\.\d+\s*-->\s*/u, '')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveReusablePrereleaseNotes(notes: string, version: string): string | undefined {
|
||||||
|
const existingBaseVersion = extractPrereleaseBaseVersionMarker(notes);
|
||||||
|
if (existingBaseVersion !== resolvePrereleaseBaseVersion(version)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return stripPrereleaseMetadata(notes);
|
||||||
|
}
|
||||||
|
|
||||||
function verifyRequestedVersionMatchesPackageVersion(
|
function verifyRequestedVersionMatchesPackageVersion(
|
||||||
options: Pick<ChangelogOptions, 'cwd' | 'version' | 'deps'>,
|
options: Pick<ChangelogOptions, 'cwd' | 'version' | 'deps'>,
|
||||||
): void {
|
): void {
|
||||||
@@ -669,13 +703,16 @@ function renderReleaseNotes(
|
|||||||
disclaimer?: string;
|
disclaimer?: string;
|
||||||
contributions?: Contribution[];
|
contributions?: Contribution[];
|
||||||
contributorSections?: string[];
|
contributorSections?: string[];
|
||||||
|
metadata?: string[];
|
||||||
},
|
},
|
||||||
): string {
|
): string {
|
||||||
const prefix = options?.disclaimer ? [options.disclaimer, ''] : [];
|
const prefix = options?.disclaimer ? [options.disclaimer, ''] : [];
|
||||||
|
const metadata = options?.metadata?.length ? [...options.metadata, ''] : [];
|
||||||
const contributorSections =
|
const contributorSections =
|
||||||
options?.contributorSections ?? renderContributorsSections(options?.contributions ?? []);
|
options?.contributorSections ?? renderContributorsSections(options?.contributions ?? []);
|
||||||
return [
|
return [
|
||||||
...prefix,
|
...prefix,
|
||||||
|
...metadata,
|
||||||
'## Highlights',
|
'## Highlights',
|
||||||
changes,
|
changes,
|
||||||
'',
|
'',
|
||||||
@@ -705,6 +742,7 @@ function writeReleaseNotesFile(
|
|||||||
outputPath?: string;
|
outputPath?: string;
|
||||||
contributions?: Contribution[];
|
contributions?: Contribution[];
|
||||||
contributorSections?: string[];
|
contributorSections?: string[];
|
||||||
|
metadata?: string[];
|
||||||
},
|
},
|
||||||
): string {
|
): string {
|
||||||
const mkdirSync = deps?.mkdirSync ?? fs.mkdirSync;
|
const mkdirSync = deps?.mkdirSync ?? fs.mkdirSync;
|
||||||
@@ -1038,7 +1076,7 @@ export function writePrereleaseNotesForVersion(options?: ChangelogOptions): stri
|
|||||||
|
|
||||||
const prereleaseNotesPath = path.join(cwd, PRERELEASE_NOTES_PATH);
|
const prereleaseNotesPath = path.join(cwd, PRERELEASE_NOTES_PATH);
|
||||||
const existingReleaseNotes = existsSync(prereleaseNotesPath)
|
const existingReleaseNotes = existsSync(prereleaseNotesPath)
|
||||||
? readFileSync(prereleaseNotesPath, 'utf8')
|
? resolveReusablePrereleaseNotes(readFileSync(prereleaseNotesPath, 'utf8'), version)
|
||||||
: undefined;
|
: undefined;
|
||||||
const changes = polishFragmentsWithClaude(fragments, {
|
const changes = polishFragmentsWithClaude(fragments, {
|
||||||
mode: 'release-notes',
|
mode: 'release-notes',
|
||||||
@@ -1052,6 +1090,7 @@ export function writePrereleaseNotesForVersion(options?: ChangelogOptions): stri
|
|||||||
'> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.',
|
'> This is a prerelease build for testing. Stable changelog and docs-site updates remain pending until the final stable release.',
|
||||||
outputPath: PRERELEASE_NOTES_PATH,
|
outputPath: PRERELEASE_NOTES_PATH,
|
||||||
contributions,
|
contributions,
|
||||||
|
metadata: [renderPrereleaseBaseVersionMarker(version)],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user