feat: support previous_tag for generate_release_notes (#372)

* feat: add generate from latest tag

* chore: refresh previous_tag docs and bundle

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: Rui Chen <rui@chenrui.dev>
This commit is contained in:
Paulo Cesar
2026-03-15 02:17:21 -03:00
committed by GitHub
parent 1853d73993
commit 9312864490
7 changed files with 329 additions and 277 deletions

View File

@@ -2,6 +2,7 @@ import {
asset,
findTagFromReleases,
finalizeRelease,
GitHubReleaser,
mimeOrDefault,
release,
Release,
@@ -32,6 +33,7 @@ describe('github', () => {
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_previous_tag: undefined,
input_append_body: false,
input_make_latest: undefined,
};
@@ -146,6 +148,86 @@ describe('github', () => {
});
});
describe('GitHubReleaser', () => {
it('passes previous_tag_name to generateReleaseNotes and strips it from createRelease', async () => {
const generateReleaseNotes = vi.fn(async () => ({
data: {
name: 'Generated release',
body: "## What's Changed\n* Added support for previous_tag",
},
}));
const createRelease = vi.fn(async (params) => ({
data: {
id: 1,
upload_url: 'test',
html_url: 'test',
tag_name: params.tag_name,
name: params.name,
body: params.body,
target_commitish: params.target_commitish || 'main',
draft: params.draft ?? false,
prerelease: params.prerelease ?? false,
assets: [],
},
}));
const releaser = new GitHubReleaser({
rest: {
repos: {
generateReleaseNotes,
createRelease,
updateRelease: vi.fn(),
getReleaseByTag: vi.fn(),
listReleaseAssets: vi.fn(),
deleteReleaseAsset: vi.fn(),
deleteRelease: vi.fn(),
updateReleaseAsset: vi.fn(),
listReleases: {
endpoint: {
merge: vi.fn(),
},
},
},
},
paginate: {
iterator: vi.fn(),
},
request: vi.fn(),
} as any);
await releaser.createRelease({
owner: 'owner',
repo: 'repo',
tag_name: 'v1.0.0',
name: 'v1.0.0',
body: 'Intro',
draft: false,
prerelease: false,
target_commitish: 'abc123',
discussion_category_name: undefined,
generate_release_notes: true,
make_latest: undefined,
previous_tag_name: 'v0.9.0',
});
expect(generateReleaseNotes).toHaveBeenCalledWith({
owner: 'owner',
repo: 'repo',
tag_name: 'v1.0.0',
target_commitish: 'abc123',
previous_tag_name: 'v0.9.0',
});
expect(createRelease).toHaveBeenCalledWith(
expect.objectContaining({
tag_name: 'v1.0.0',
body: "Intro\n\n## What's Changed\n* Added support for previous_tag",
generate_release_notes: false,
}),
);
expect(createRelease.mock.calls[0][0]).not.toHaveProperty('previous_tag_name');
});
});
describe('finalizeRelease input_draft behavior', () => {
const draftRelease: Release = {
id: 1,
@@ -340,6 +422,101 @@ describe('github', () => {
});
describe('error handling', () => {
it('passes previous_tag_name through when creating a release with generated notes', async () => {
const createReleaseSpy = vi.fn(async () => ({
data: {
id: 1,
upload_url: 'test',
html_url: 'test',
tag_name: 'v1.0.0',
name: 'test',
body: 'generated notes',
target_commitish: 'main',
draft: true,
prerelease: false,
assets: [],
},
}));
await release(
{
...config,
input_generate_release_notes: true,
input_previous_tag: 'v0.9.0',
},
{
getReleaseByTag: () => Promise.reject({ status: 404 }),
createRelease: createReleaseSpy,
updateRelease: () => Promise.reject('Not implemented'),
finalizeRelease: () => Promise.reject('Not implemented'),
allReleases: async function* () {
yield { data: [] };
},
listReleaseAssets: () => Promise.reject('Not implemented'),
deleteReleaseAsset: () => Promise.reject('Not implemented'),
deleteRelease: () => Promise.reject('Not implemented'),
updateReleaseAsset: () => Promise.reject('Not implemented'),
uploadReleaseAsset: () => Promise.reject('Not implemented'),
},
1,
);
expect(createReleaseSpy).toHaveBeenCalledWith(
expect.objectContaining({
tag_name: 'v1.0.0',
generate_release_notes: true,
previous_tag_name: 'v0.9.0',
}),
);
});
it('passes previous_tag_name through when updating a release with generated notes', async () => {
const existingRelease: Release = {
id: 1,
upload_url: 'test',
html_url: 'test',
tag_name: 'v1.0.0',
name: 'test',
body: 'existing body',
target_commitish: 'main',
draft: false,
prerelease: false,
assets: [],
};
const updateReleaseSpy = vi.fn(async () => ({ data: existingRelease }));
await release(
{
...config,
input_generate_release_notes: true,
input_previous_tag: 'v0.9.0',
},
{
getReleaseByTag: () => Promise.resolve({ data: existingRelease }),
createRelease: () => Promise.reject('Not implemented'),
updateRelease: updateReleaseSpy,
finalizeRelease: () => Promise.reject('Not implemented'),
allReleases: async function* () {
yield { data: [existingRelease] };
},
listReleaseAssets: () => Promise.reject('Not implemented'),
deleteReleaseAsset: () => Promise.reject('Not implemented'),
deleteRelease: () => Promise.reject('Not implemented'),
updateReleaseAsset: () => Promise.reject('Not implemented'),
uploadReleaseAsset: () => Promise.reject('Not implemented'),
},
1,
);
expect(updateReleaseSpy).toHaveBeenCalledWith(
expect.objectContaining({
release_id: existingRelease.id,
generate_release_notes: true,
previous_tag_name: 'v0.9.0',
}),
);
});
it('creates published prereleases without the forced draft-first path', async () => {
const prereleaseConfig = {
...config,

View File

@@ -174,6 +174,29 @@ describe('util', () => {
});
});
describe('parseConfig', () => {
const baseParsedConfig = {
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_previous_tag: undefined,
input_make_latest: undefined,
};
it('parses basic config', () => {
assert.deepStrictEqual(
parseConfig({
@@ -186,27 +209,7 @@ describe('util', () => {
INPUT_TARGET_COMMITISH: '',
INPUT_DISCUSSION_CATEGORY_NAME: '',
}),
{
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
},
baseParsedConfig,
);
});
@@ -216,25 +219,8 @@ describe('util', () => {
INPUT_TARGET_COMMITISH: 'affa18ef97bc9db20076945705aba8c516139abd',
}),
{
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_files: [],
input_overwrite_files: undefined,
input_preserve_order: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
...baseParsedConfig,
input_target_commitish: 'affa18ef97bc9db20076945705aba8c516139abd',
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
},
);
});
@@ -244,25 +230,8 @@ describe('util', () => {
INPUT_DISCUSSION_CATEGORY_NAME: 'releases',
}),
{
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_files: [],
input_preserve_order: undefined,
input_name: undefined,
input_overwrite_files: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
...baseParsedConfig,
input_discussion_category_name: 'releases',
input_generate_release_notes: false,
input_make_latest: undefined,
},
);
});
@@ -273,25 +242,20 @@ describe('util', () => {
INPUT_GENERATE_RELEASE_NOTES: 'true',
}),
{
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
...baseParsedConfig,
input_generate_release_notes: true,
input_make_latest: undefined,
},
);
});
it('supports an explicit previous tag for release notes generation', () => {
assert.deepStrictEqual(
parseConfig({
INPUT_PREVIOUS_TAG: ' v1.2.3 ',
}),
{
...baseParsedConfig,
input_previous_tag: 'v1.2.3',
},
);
});
@@ -306,25 +270,11 @@ describe('util', () => {
INPUT_TOKEN: 'input-token',
}),
{
github_ref: '',
github_repository: '',
...baseParsedConfig,
github_token: 'input-token',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: false,
input_prerelease: true,
input_preserve_order: true,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
},
);
});
@@ -335,25 +285,8 @@ describe('util', () => {
INPUT_TOKEN: ' ',
}),
{
github_ref: '',
github_repository: '',
...baseParsedConfig,
github_token: 'env-token',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
},
);
});
@@ -365,25 +298,10 @@ describe('util', () => {
INPUT_TOKEN: 'input-token',
}),
{
github_ref: '',
github_repository: '',
...baseParsedConfig,
github_token: 'input-token',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: false,
input_prerelease: true,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
},
);
});
@@ -394,25 +312,9 @@ describe('util', () => {
INPUT_PRERELEASE: 'true',
}),
{
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
...baseParsedConfig,
input_draft: false,
input_prerelease: true,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
},
);
});
@@ -422,24 +324,7 @@ describe('util', () => {
INPUT_MAKE_LATEST: 'false',
}),
{
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_name: undefined,
input_overwrite_files: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
...baseParsedConfig,
input_make_latest: 'false',
},
);
@@ -450,25 +335,8 @@ describe('util', () => {
INPUT_APPEND_BODY: 'true',
}),
{
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
...baseParsedConfig,
input_append_body: true,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
},
);
});