Fixes #548 Add support body + generated release notes (#549)

This commit is contained in:
Nick Cipollo
2025-09-02 12:27:56 -04:00
committed by GitHub
parent 571fe81601
commit d3601261f8
4 changed files with 104 additions and 40 deletions

View File

@@ -85,7 +85,7 @@ describe("Action", () => {
}) })
it("creates release with generated release notes that override existing body", async () => { it("creates release with generated release notes that override existing body", async () => {
const action = createAction(false, false, false, true, false, "existing body") const action = createAction(false, false, false, true, false, "")
await action.perform() await action.perform()
@@ -126,8 +126,29 @@ describe("Action", () => {
assertAssetUrlsApplied({}) assertAssetUrlsApplied({})
}) })
it("creates release with combined body and generated release notes", async () => {
const action = createAction(false, false, false, true, false, createBody)
await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(createMock).toHaveBeenCalledWith(
tag,
`${createBody}\n${generatedReleaseBody}`,
commit,
discussionCategory,
createDraft,
makeLatest,
createName,
createPrerelease
)
expect(uploadMock).not.toHaveBeenCalled()
assertOutputApplied()
assertAssetUrlsApplied({})
})
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) const action = createAction(false, false, false, true, false, "")
await action.perform() await action.perform()
@@ -148,7 +169,7 @@ describe("Action", () => {
}) })
it("creates release if no release exists to update", async () => { it("creates release if no release exists to update", async () => {
const action = createAction(true, true) const action = createAction(true, true, false, true, false, "")
const error = { status: 404 } const error = { status: 404 }
getMock.mockRejectedValue(error) getMock.mockRejectedValue(error)
@@ -174,7 +195,7 @@ describe("Action", () => {
}) })
it("creates release if no draft releases", async () => { it("creates release if no draft releases", async () => {
const action = createAction(true, true) const action = createAction(true, true, false, true, false, "")
const error = { status: 404 } const error = { status: 404 }
getMock.mockRejectedValue(error) getMock.mockRejectedValue(error)
listMock.mockResolvedValue({ listMock.mockResolvedValue({
@@ -203,7 +224,7 @@ describe("Action", () => {
}) })
it("creates release then uploads artifact", async () => { it("creates release then uploads artifact", async () => {
const action = createAction(false, true) const action = createAction(false, true, false, true, false, "")
await action.perform() await action.perform()
@@ -263,7 +284,7 @@ describe("Action", () => {
}) })
it("throws error when create fails", async () => { it("throws error when create fails", async () => {
const action = createAction(false, true) const action = createAction(false, true, false, true, false, "")
createMock.mockRejectedValue("error") createMock.mockRejectedValue("error")
expect.hasAssertions() expect.hasAssertions()
@@ -337,7 +358,7 @@ describe("Action", () => {
}) })
it("throws error when update fails", async () => { it("throws error when update fails", async () => {
const action = createAction(true, true) const action = createAction(true, true, false, true, false, "", true, createDraft, "")
updateMock.mockRejectedValue("error") updateMock.mockRejectedValue("error")
@@ -363,7 +384,7 @@ describe("Action", () => {
}) })
it("throws error when upload fails", async () => { it("throws error when upload fails", async () => {
const action = createAction(false, true) const action = createAction(false, true, false, true, false, "")
const expectedError = { status: 404 } const expectedError = { status: 404 }
uploadMock.mockRejectedValue(expectedError) uploadMock.mockRejectedValue(expectedError)
@@ -389,7 +410,7 @@ describe("Action", () => {
}) })
it("updates draft release", async () => { it("updates draft release", async () => {
const action = createAction(true, true) const action = createAction(true, true, false, true, false, "", true, createDraft, "")
const error = { status: 404 } const error = { status: 404 }
getMock.mockRejectedValue(error) getMock.mockRejectedValue(error)
listMock.mockResolvedValue({ listMock.mockResolvedValue({
@@ -452,8 +473,33 @@ describe("Action", () => {
}) })
}) })
it("updates release with combined body and generated release notes", async () => {
const action = createAction(true, true, false, true, false, "", true, createDraft, updateBody)
await action.perform()
expect(genReleaseNotesMock).toHaveBeenCalledWith(tag)
expect(updateMock).toHaveBeenCalledWith(
id,
tag,
`${updateBody}\n${generatedReleaseBody}`,
commit,
discussionCategory,
updateDraft,
makeLatest,
updateName,
updatePrerelease
)
expect(uploadMock).toHaveBeenCalledWith(artifacts, releaseId, url)
assertOutputApplied()
assertAssetUrlsApplied({
"art1": "https://github.com/owner/repo/releases/download/v1.0.0/art1",
"art2": "https://github.com/owner/repo/releases/download/v1.0.0/art2",
})
})
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) const action = createAction(true, false, false, true, false, "", true, createDraft, "")
await action.perform() await action.perform()
@@ -474,7 +520,7 @@ describe("Action", () => {
}) })
it("updates release then uploads artifact", async () => { it("updates release then uploads artifact", async () => {
const action = createAction(true, true) const action = createAction(true, true, false, true, false, "", true, createDraft, "")
await action.perform() await action.perform()
@@ -530,7 +576,7 @@ describe("Action", () => {
}) })
it("does not publish immutable release when immutableCreate is false", async () => { it("does not publish immutable release when immutableCreate is false", async () => {
const action = createAction(false, true, false, true, false, createBody, false, false) const action = createAction(false, true, false, true, false, "", false, false)
await action.perform() await action.perform()
@@ -550,7 +596,7 @@ describe("Action", () => {
}) })
it("does not publish immutable release when createdDraft is true", async () => { it("does not publish immutable release when createdDraft is true", async () => {
const action = createAction(false, true, false, true, false, createBody, true, true) const action = createAction(false, true, false, true, false, "", true, true)
await action.perform() await action.perform()
@@ -570,7 +616,7 @@ describe("Action", () => {
}) })
it("publishes immutable release when immutableCreate is true and createdDraft is false", async () => { it("publishes immutable release when immutableCreate is true and createdDraft is false", async () => {
const action = createAction(false, true, false, true, false, createBody, true, false) const action = createAction(false, true, false, true, false, "", true, false)
const immutableReleaseResponse = { const immutableReleaseResponse = {
data: { data: {
id: 999, id: 999,
@@ -615,7 +661,7 @@ describe("Action", () => {
}) })
it("publishes immutable release when allowUpdates is true but release does not exist", async () => { it("publishes immutable release when allowUpdates is true but release does not exist", async () => {
const action = createAction(true, true, false, true, false, createBody, true, false) const action = createAction(true, true, false, true, false, "", true, false)
const error = { status: 404 } const error = { status: 404 }
getMock.mockRejectedValue(error) getMock.mockRejectedValue(error)
listMock.mockResolvedValue({ data: [] }) // No draft releases found listMock.mockResolvedValue({ data: [] }) // No draft releases found
@@ -690,7 +736,8 @@ describe("Action", () => {
omitBodyDuringUpdate = false, omitBodyDuringUpdate = false,
createdReleaseBody = createBody, createdReleaseBody = createBody,
immutableCreate = true, immutableCreate = true,
createdDraft = createDraft createdDraft = createDraft,
updatedReleaseBody = updateBody
): Action { ): Action {
let inputArtifact: Artifact[] let inputArtifact: Artifact[]
@@ -773,7 +820,7 @@ describe("Action", () => {
tag, tag,
token, token,
updatedDraft: updateDraft, updatedDraft: updateDraft,
updatedReleaseBody: updateBody, updatedReleaseBody: updatedReleaseBody,
updatedReleaseName: updateName, updatedReleaseName: updateName,
updatedPrerelease: updatePrerelease, updatedPrerelease: updatePrerelease,
updateOnlyUnreleased: updateOnlyUnreleased, updateOnlyUnreleased: updateOnlyUnreleased,

27
dist/index.js vendored
View File

@@ -118,20 +118,12 @@ class Action {
return draftRelease?.id; return draftRelease?.id;
} }
async updateRelease(id) { async updateRelease(id) {
let releaseBody = this.inputs.updatedReleaseBody; const releaseBody = await this.combineBodyWithReleaseNotes(this.inputs.updatedReleaseBody, true);
if (this.inputs.generateReleaseNotes && !this.inputs.omitBodyDuringUpdate) {
const response = await this.releases.generateReleaseNotes(this.inputs.tag);
releaseBody = response.data.body;
}
const releaseResponse = await this.releases.update(id, this.inputs.tag, releaseBody, this.inputs.commit, this.inputs.discussionCategory, this.inputs.updatedDraft, this.inputs.makeLatest, this.inputs.updatedReleaseName, this.inputs.updatedPrerelease); const releaseResponse = await this.releases.update(id, this.inputs.tag, releaseBody, this.inputs.commit, this.inputs.discussionCategory, this.inputs.updatedDraft, this.inputs.makeLatest, this.inputs.updatedReleaseName, this.inputs.updatedPrerelease);
await this.processReleaseArtifactsAndOutputs(releaseResponse, false); await this.processReleaseArtifactsAndOutputs(releaseResponse, false);
} }
async createRelease() { async createRelease() {
let releaseBody = this.inputs.createdReleaseBody; const releaseBody = await this.combineBodyWithReleaseNotes(this.inputs.createdReleaseBody, false);
if (this.inputs.generateReleaseNotes) {
const response = await this.releases.generateReleaseNotes(this.inputs.tag);
releaseBody = response.data.body;
}
// If immutableCreate is enabled we need to start with a draft release // If immutableCreate is enabled we need to start with a draft release
const draft = this.inputs.createdDraft || this.inputs.immutableCreate; const draft = this.inputs.createdDraft || this.inputs.immutableCreate;
const releaseResponse = await this.releases.create(this.inputs.tag, releaseBody, this.inputs.commit, this.inputs.discussionCategory, draft, this.inputs.makeLatest, this.inputs.createdReleaseName, this.inputs.createdPrerelease); const releaseResponse = await this.releases.create(this.inputs.tag, releaseBody, this.inputs.commit, this.inputs.discussionCategory, draft, this.inputs.makeLatest, this.inputs.createdReleaseName, this.inputs.createdPrerelease);
@@ -168,6 +160,21 @@ class Action {
this.inputs.discussionCategory, false, // We want to publish the release, set draft to false this.inputs.discussionCategory, false, // We want to publish the release, set draft to false
this.inputs.makeLatest, this.inputs.createdReleaseName, this.inputs.createdPrerelease); this.inputs.makeLatest, this.inputs.createdReleaseName, this.inputs.createdPrerelease);
} }
async combineBodyWithReleaseNotes(body, isUpdate) {
// Determine if we should generate release notes based on operation type
const shouldGenerateReleaseNotes = isUpdate
? this.inputs.generateReleaseNotes && !this.inputs.omitBodyDuringUpdate
: this.inputs.generateReleaseNotes;
if (!shouldGenerateReleaseNotes) {
return body;
}
const response = await this.releases.generateReleaseNotes(this.inputs.tag);
const releaseNotes = response.data.body;
if (!body || body.trim() === "") {
return releaseNotes;
}
return `${body}\n${releaseNotes}`;
}
setOutputs(releaseData, assetUrls) { setOutputs(releaseData, assetUrls) {
this.outputs.applyReleaseData(releaseData); this.outputs.applyReleaseData(releaseData);
this.outputs.applyAssetUrls(assetUrls); this.outputs.applyAssetUrls(assetUrls);

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@@ -105,12 +105,7 @@ export class Action {
} }
private async updateRelease(id: number) { private async updateRelease(id: number) {
let releaseBody = this.inputs.updatedReleaseBody const releaseBody = await this.combineBodyWithReleaseNotes(this.inputs.updatedReleaseBody, true)
if (this.inputs.generateReleaseNotes && !this.inputs.omitBodyDuringUpdate) {
const response = await this.releases.generateReleaseNotes(this.inputs.tag)
releaseBody = response.data.body
}
const releaseResponse = await this.releases.update( const releaseResponse = await this.releases.update(
id, id,
@@ -128,12 +123,7 @@ export class Action {
} }
private async createRelease() { private async createRelease() {
let releaseBody = this.inputs.createdReleaseBody const releaseBody = await this.combineBodyWithReleaseNotes(this.inputs.createdReleaseBody, false)
if (this.inputs.generateReleaseNotes) {
const response = await this.releases.generateReleaseNotes(this.inputs.tag)
releaseBody = response.data.body
}
// If immutableCreate is enabled we need to start with a draft release // If immutableCreate is enabled we need to start with a draft release
const draft = this.inputs.createdDraft || this.inputs.immutableCreate const draft = this.inputs.createdDraft || this.inputs.immutableCreate
@@ -197,6 +187,26 @@ export class Action {
) )
} }
private async combineBodyWithReleaseNotes(body: string | undefined, isUpdate: boolean): Promise<string | undefined> {
// Determine if we should generate release notes based on operation type
const shouldGenerateReleaseNotes = isUpdate
? this.inputs.generateReleaseNotes && !this.inputs.omitBodyDuringUpdate
: this.inputs.generateReleaseNotes
if (!shouldGenerateReleaseNotes) {
return body
}
const response = await this.releases.generateReleaseNotes(this.inputs.tag)
const releaseNotes = response.data.body
if (!body || body.trim() === "") {
return releaseNotes
}
return `${body}\n${releaseNotes}`
}
private setOutputs(releaseData: any, assetUrls: Record<string, string>): void { private setOutputs(releaseData: any, assetUrls: Record<string, string>): void {
this.outputs.applyReleaseData(releaseData) this.outputs.applyReleaseData(releaseData)
this.outputs.applyAssetUrls(assetUrls) this.outputs.applyAssetUrls(assetUrls)