Fixes #542 Add previous tag option (#550)

This commit is contained in:
Nick Cipollo
2025-09-02 15:56:40 -04:00
committed by GitHub
parent 98d25d4189
commit e87de4c2e4
8 changed files with 99 additions and 21 deletions

View File

@@ -49,6 +49,7 @@ const updateOnlyUnreleased = false
const url = TEST_URLS.UPLOAD_URL const url = TEST_URLS.UPLOAD_URL
const makeLatest = "legacy" const makeLatest = "legacy"
const generatedReleaseBody = "test release notes" const generatedReleaseBody = "test release notes"
const previousTag = "v1.0.0"
describe("Action", () => { describe("Action", () => {
beforeEach(() => { beforeEach(() => {
@@ -68,7 +69,7 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
generatedReleaseBody, generatedReleaseBody,
@@ -89,7 +90,7 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
generatedReleaseBody, generatedReleaseBody,
@@ -131,7 +132,7 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
`${createBody}\n${generatedReleaseBody}`, `${createBody}\n${generatedReleaseBody}`,
@@ -147,12 +148,31 @@ describe("Action", () => {
assertAssetUrlsApplied({}) assertAssetUrlsApplied({})
}) })
it("creates release with combined body and generated release notes using previous tag", async () => {
const action = createAction(false, false, false, true, false, createBody, true, createDraft, updateBody, previousTag)
await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, previousTag)
expect(createMock).toHaveBeenCalledWith(
tag,
`${createBody}\n${generatedReleaseBody}`,
commit,
discussionCategory,
createDraft,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).not.toHaveBeenCalled()
})
it("creates release but does not upload if no artifact", async () => { it("creates release but does not upload if no artifact", async () => {
const action = createAction(false, false, false, true, false, "") const action = createAction(false, false, false, true, false, "")
await action.perform() await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
generatedReleaseBody, generatedReleaseBody,
@@ -175,7 +195,7 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
generatedReleaseBody, generatedReleaseBody,
@@ -204,7 +224,7 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
generatedReleaseBody, generatedReleaseBody,
@@ -228,7 +248,7 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
generatedReleaseBody, generatedReleaseBody,
@@ -294,7 +314,7 @@ describe("Action", () => {
expect(error).toEqual("error") expect(error).toEqual("error")
} }
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
generatedReleaseBody, generatedReleaseBody,
@@ -395,7 +415,7 @@ describe("Action", () => {
expect(error).toEqual(expectedError) expect(error).toEqual(expectedError)
} }
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(createMock).toHaveBeenCalledWith( expect(createMock).toHaveBeenCalledWith(
tag, tag,
generatedReleaseBody, generatedReleaseBody,
@@ -478,7 +498,7 @@ describe("Action", () => {
await action.perform() await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag) expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, undefined)
expect(updateMock).toHaveBeenCalledWith( expect(updateMock).toHaveBeenCalledWith(
id, id,
tag, tag,
@@ -498,6 +518,26 @@ describe("Action", () => {
}) })
}) })
it("updates release with combined body and generated release notes using previous tag", async () => {
const action = createAction(true, true, false, true, false, "", true, createDraft, updateBody, previousTag)
await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag, previousTag)
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
`${updateBody}\n${generatedReleaseBody}`,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
})
it("updates release but does not upload if no artifact", async () => { it("updates release but does not upload if no artifact", async () => {
const action = createAction(true, false, false, true, false, "", true, createDraft, "") const action = createAction(true, false, false, true, false, "", true, createDraft, "")
@@ -737,7 +777,8 @@ describe("Action", () => {
createdReleaseBody = createBody, createdReleaseBody = createBody,
immutableCreate = true, immutableCreate = true,
createdDraft = createDraft, createdDraft = createDraft,
updatedReleaseBody = updateBody updatedReleaseBody = updateBody,
generateReleaseNotesPreviousTag: string | undefined = undefined
): Action { ): Action {
let inputArtifact: Artifact[] let inputArtifact: Artifact[]
@@ -809,6 +850,7 @@ describe("Action", () => {
commit, commit,
discussionCategory, discussionCategory,
generateReleaseNotes, generateReleaseNotes,
generateReleaseNotesPreviousTag: generateReleaseNotesPreviousTag,
immutableCreate: immutableCreate, immutableCreate: immutableCreate,
makeLatest: makeLatest, makeLatest: makeLatest,
owner: "owner", owner: "owner",

View File

@@ -200,6 +200,18 @@ describe("Inputs", () => {
}) })
}) })
describe("generateReleaseNotesPreviousTag", () => {
it("returns the previous tag when provided", function () {
mockGetInput.mockReturnValue("v1.0.0")
expect(inputs.generateReleaseNotesPreviousTag).toBe("v1.0.0")
})
it("returns undefined when omitted", function () {
mockGetInput.mockReturnValue("")
expect(inputs.generateReleaseNotesPreviousTag).toBeUndefined()
})
})
describe("immutableCreate", () => { describe("immutableCreate", () => {
it("returns false by default", function () { it("returns false by default", function () {
mockGetInput.mockReturnValue("") mockGetInput.mockReturnValue("")

View File

@@ -47,6 +47,10 @@ inputs:
description: 'Indicates if release notes should be automatically generated.' description: 'Indicates if release notes should be automatically generated.'
required: false required: false
default: 'false' default: 'false'
generateReleaseNotesPreviousTag:
description: 'An optional previous tag to use when generating release notes. This will limit the release notes to changes between the two tags.'
required: false
default: ''
immutableCreate: immutableCreate:
description: 'Indicates if immutable release creation should be used. When enabled, the action will first create a draft, upload artifacts, then publish the release.' description: 'Indicates if immutable release creation should be used. When enabled, the action will first create a draft, upload artifacts, then publish the release.'
required: false required: false

16
dist/index.js vendored
View File

@@ -168,7 +168,7 @@ class Action {
if (!shouldGenerateReleaseNotes) { if (!shouldGenerateReleaseNotes) {
return body; return body;
} }
const response = await this.releases.generateReleaseNotes(this.inputs.tag); const response = await this.releases.generateReleaseNotes(this.inputs.tag, this.inputs.generateReleaseNotesPreviousTag);
const releaseNotes = response.data.body; const releaseNotes = response.data.body;
if (!body || body.trim() === "") { if (!body || body.trim() === "") {
return releaseNotes; return releaseNotes;
@@ -867,6 +867,10 @@ class CoreInputs {
const generate = core.getInput("generateReleaseNotes"); const generate = core.getInput("generateReleaseNotes");
return generate == "true"; return generate == "true";
} }
get generateReleaseNotesPreviousTag() {
const previousTag = core.getInput("generateReleaseNotesPreviousTag");
return previousTag || undefined;
}
get immutableCreate() { get immutableCreate() {
const immutable = core.getInput("immutableCreate"); const immutable = core.getInput("immutableCreate");
return immutable == "true"; return immutable == "true";
@@ -1107,12 +1111,16 @@ class GithubReleases {
repo: this.inputs.repo, repo: this.inputs.repo,
}); });
} }
async generateReleaseNotes(tag) { async generateReleaseNotes(tag, previousTag) {
return this.git.rest.repos.generateReleaseNotes({ const params = {
owner: this.inputs.owner, owner: this.inputs.owner,
repo: this.inputs.repo, repo: this.inputs.repo,
tag_name: tag, tag_name: tag,
}); };
if (previousTag) {
params.previous_tag_name = previousTag;
}
return this.git.rest.repos.generateReleaseNotes(params);
} }
async getByTag(tag) { async getByTag(tag) {
return this.git.rest.repos.getReleaseByTag({ return this.git.rest.repos.getReleaseByTag({

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@@ -197,7 +197,7 @@ export class Action {
return body return body
} }
const response = await this.releases.generateReleaseNotes(this.inputs.tag) const response = await this.releases.generateReleaseNotes(this.inputs.tag, this.inputs.generateReleaseNotesPreviousTag)
const releaseNotes = response.data.body const releaseNotes = response.data.body
if (!body || body.trim() === "") { if (!body || body.trim() === "") {

View File

@@ -15,6 +15,7 @@ export interface Inputs {
readonly createdReleaseName?: string readonly createdReleaseName?: string
readonly discussionCategory?: string readonly discussionCategory?: string
readonly generateReleaseNotes: boolean readonly generateReleaseNotes: boolean
readonly generateReleaseNotesPreviousTag?: string
readonly immutableCreate: boolean readonly immutableCreate: boolean
readonly makeLatest?: "legacy" | "true" | "false" | undefined readonly makeLatest?: "legacy" | "true" | "false" | undefined
readonly omitBodyDuringUpdate: boolean readonly omitBodyDuringUpdate: boolean
@@ -138,6 +139,11 @@ export class CoreInputs implements Inputs {
return generate == "true" return generate == "true"
} }
get generateReleaseNotesPreviousTag(): string | undefined {
const previousTag = core.getInput("generateReleaseNotesPreviousTag")
return previousTag || undefined
}
get immutableCreate(): boolean { get immutableCreate(): boolean {
const immutable = core.getInput("immutableCreate") const immutable = core.getInput("immutableCreate")
return immutable == "true" return immutable == "true"

View File

@@ -36,7 +36,7 @@ export interface Releases {
getByTag(tag: string): Promise<ReleaseByTagResponse> getByTag(tag: string): Promise<ReleaseByTagResponse>
generateReleaseNotes(tag: string): Promise<GenerateReleaseNotesResponse> generateReleaseNotes(tag: string, previousTag?: string): Promise<GenerateReleaseNotesResponse>
listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponseData> listArtifactsForRelease(releaseId: number): Promise<ListReleaseAssetsResponseData>
@@ -106,12 +106,18 @@ export class GithubReleases implements Releases {
}) })
} }
async generateReleaseNotes(tag: string): Promise<GenerateReleaseNotesResponse> { async generateReleaseNotes(tag: string, previousTag?: string): Promise<GenerateReleaseNotesResponse> {
return this.git.rest.repos.generateReleaseNotes({ const params: any = {
owner: this.inputs.owner, owner: this.inputs.owner,
repo: this.inputs.repo, repo: this.inputs.repo,
tag_name: tag, tag_name: tag,
}) }
if (previousTag) {
params.previous_tag_name = previousTag
}
return this.git.rest.repos.generateReleaseNotes(params)
} }
async getByTag(tag: string): Promise<ReleaseByTagResponse> { async getByTag(tag: string): Promise<ReleaseByTagResponse> {