From 04d64ce68ecd0b5b6c557ec9a67b9149ae90e74c Mon Sep 17 00:00:00 2001 From: Nick Cipollo Date: Thu, 2 Jan 2020 22:03:30 -0500 Subject: [PATCH] Fixes #8 Add support for draft release updating --- __tests__/Action.test.ts | 45 ++++++++++++++++++++++++++++--- lib/Action.js | 45 ++++++++++++++++++++++--------- lib/Releases.js | 8 ++++++ src/Action.ts | 58 ++++++++++++++++++++++++++-------------- src/Releases.ts | 11 +++++++- 5 files changed, 131 insertions(+), 36 deletions(-) diff --git a/__tests__/Action.test.ts b/__tests__/Action.test.ts index d3fee12..ae638a5 100644 --- a/__tests__/Action.test.ts +++ b/__tests__/Action.test.ts @@ -6,6 +6,7 @@ import { ArtifactUploader } from "../src/ArtifactUploader"; const createMock = jest.fn() const getMock = jest.fn() +const listMock = jest.fn() const updateMock = jest.fn() const uploadMock = jest.fn() @@ -28,6 +29,7 @@ describe("Action", () => { beforeEach(() => { createMock.mockClear() getMock.mockClear() + listMock.mockClear() updateMock.mockClear() uploadMock.mockClear() }) @@ -43,9 +45,7 @@ describe("Action", () => { it('creates release if no release exists to update', async () => { const action = createAction(true, true) - const error = { - status: 404 - } + const error = { status: 404 } getMock.mockRejectedValue(error) await action.perform() @@ -54,6 +54,23 @@ describe("Action", () => { expect(uploadMock).toBeCalledWith(artifacts, url) }) + it('creates release if no draft releases', async () => { + const action = createAction(true, true) + const error = { status: 404 } + getMock.mockRejectedValue(error) + listMock.mockResolvedValue({ + data: [ + { id: id, draft: false, tag_name: tag } + ] + }) + + await action.perform() + + expect(createMock).toBeCalledWith(tag, body, commit, draft, name, prerelease) + expect(uploadMock).toBeCalledWith(artifacts, url) + + }) + it('creates release then uploads artifact', async () => { const action = createAction(false, true) @@ -134,6 +151,24 @@ describe("Action", () => { expect(uploadMock).toBeCalledWith(artifacts, url) }) + it('updates draft release', async () => { + const action = createAction(true, true) + const error = { status: 404 } + getMock.mockRejectedValue(error) + listMock.mockResolvedValue({ + data: [ + { id: 123, draft: false, tag_name: tag }, + { id: id, draft: true, tag_name: tag } + ] + }) + + await action.perform() + + expect(updateMock).toBeCalledWith(id, tag, body, commit, draft, name, prerelease) + expect(uploadMock).toBeCalledWith(artifacts, url) + + }) + it('updates release but does not upload if no artifact', async () => { const action = createAction(true, false) @@ -165,6 +200,7 @@ describe("Action", () => { return { create: createMock, getByTag: getMock, + listReleases: listMock, update: updateMock, uploadArtifact: uploadMock } @@ -180,6 +216,9 @@ describe("Action", () => { id: id } }) + listMock.mockResolvedValue({ + data: [] + }) updateMock.mockResolvedValue({ data: { upload_url: url diff --git a/lib/Action.js b/lib/Action.js index de1aa8e..98122b4 100644 --- a/lib/Action.js +++ b/lib/Action.js @@ -33,8 +33,8 @@ class Action { return yield this.updateRelease(getResponse.data.id); } catch (error) { - if (this.noRelease(error)) { - return yield this.createRelease(); + if (this.noPublishedRelease(error)) { + return yield this.updateDraftOrCreateRelease(); } else { throw error; @@ -46,21 +46,42 @@ class Action { } }); } - createRelease() { - return __awaiter(this, void 0, void 0, function* () { - const response = yield this.releases.create(this.inputs.tag, this.inputs.body, this.inputs.commit, this.inputs.draft, this.inputs.name, this.inputs.prerelease); - return response.data.upload_url; - }); - } - noRelease(error) { - const errorMessage = new ErrorMessage_1.ErrorMessage(error); - return errorMessage.status == 404; - } updateRelease(id) { return __awaiter(this, void 0, void 0, function* () { const response = yield this.releases.update(id, this.inputs.tag, this.inputs.body, this.inputs.commit, this.inputs.draft, this.inputs.name, this.inputs.prerelease); return response.data.upload_url; }); } + noPublishedRelease(error) { + const errorMessage = new ErrorMessage_1.ErrorMessage(error); + return errorMessage.status == 404; + } + updateDraftOrCreateRelease() { + return __awaiter(this, void 0, void 0, function* () { + const draftReleaseId = yield this.findMatchingDraftReleaseId(); + if (draftReleaseId) { + return yield this.updateRelease(draftReleaseId); + } + else { + return yield this.createRelease(); + } + }); + } + findMatchingDraftReleaseId() { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const tag = this.inputs.tag; + const response = yield this.releases.listReleases(); + const releases = response.data; + const draftRelease = releases.find(release => release.draft && release.tag_name == tag); + return (_a = draftRelease) === null || _a === void 0 ? void 0 : _a.id; + }); + } + createRelease() { + return __awaiter(this, void 0, void 0, function* () { + const response = yield this.releases.create(this.inputs.tag, this.inputs.body, this.inputs.commit, this.inputs.draft, this.inputs.name, this.inputs.prerelease); + return response.data.upload_url; + }); + } } exports.Action = Action; diff --git a/lib/Releases.js b/lib/Releases.js index 3502723..2762cb8 100644 --- a/lib/Releases.js +++ b/lib/Releases.js @@ -28,6 +28,14 @@ class GithubReleases { }); }); } + listReleases() { + return __awaiter(this, void 0, void 0, function* () { + return this.git.repos.listReleases({ + owner: this.context.repo.owner, + repo: this.context.repo.repo + }); + }); + } getByTag(tag) { return __awaiter(this, void 0, void 0, function* () { return this.git.repos.getReleaseByTag({ diff --git a/src/Action.ts b/src/Action.ts index 3877587..896e3ff 100644 --- a/src/Action.ts +++ b/src/Action.ts @@ -29,8 +29,8 @@ export class Action { const getResponse = await this.releases.getByTag(this.inputs.tag) return await this.updateRelease(getResponse.data.id) } catch (error) { - if (this.noRelease(error)) { - return await this.createRelease() + if (this.noPublishedRelease(error)) { + return await this.updateDraftOrCreateRelease() } else { throw error } @@ -40,24 +40,6 @@ export class Action { } } - private async createRelease(): Promise { - const response = await this.releases.create( - this.inputs.tag, - this.inputs.body, - this.inputs.commit, - this.inputs.draft, - this.inputs.name, - this.inputs.prerelease - ) - - return response.data.upload_url - } - - private noRelease(error: any): boolean { - const errorMessage = new ErrorMessage(error) - return errorMessage.status == 404 - } - private async updateRelease(id: number): Promise { const response = await this.releases.update( id, @@ -71,4 +53,40 @@ export class Action { return response.data.upload_url } + + private noPublishedRelease(error: any): boolean { + const errorMessage = new ErrorMessage(error) + return errorMessage.status == 404 + } + + private async updateDraftOrCreateRelease(): Promise { + const draftReleaseId = await this.findMatchingDraftReleaseId() + if (draftReleaseId) { + return await this.updateRelease(draftReleaseId) + } else { + return await this.createRelease() + } + } + + private async findMatchingDraftReleaseId(): Promise { + const tag = this.inputs.tag + const response = await this.releases.listReleases() + const releases = response.data + const draftRelease = releases.find(release => release.draft && release.tag_name == tag) + + return draftRelease?.id + } + + private async createRelease(): Promise { + const response = await this.releases.create( + this.inputs.tag, + this.inputs.body, + this.inputs.commit, + this.inputs.draft, + this.inputs.name, + this.inputs.prerelease + ) + + return response.data.upload_url + } } \ No newline at end of file diff --git a/src/Releases.ts b/src/Releases.ts index f2bfe11..5c86207 100644 --- a/src/Releases.ts +++ b/src/Releases.ts @@ -1,6 +1,6 @@ import { Context } from "@actions/github/lib/context"; import { GitHub } from "@actions/github"; -import { AnyResponse, Response, ReposCreateReleaseResponse, ReposGetReleaseByTagResponse } from "@octokit/rest"; +import { AnyResponse, Response, ReposCreateReleaseResponse, ReposGetReleaseByTagResponse, ReposListReleasesResponse } from "@octokit/rest"; export interface Releases { create( @@ -14,6 +14,8 @@ export interface Releases { getByTag(tag: string): Promise> + listReleases(): Promise> + update( id: number, tag: string, @@ -62,6 +64,13 @@ export class GithubReleases implements Releases { }) } + async listReleases(): Promise> { + return this.git.repos.listReleases({ + owner: this.context.repo.owner, + repo: this.context.repo.repo + }) + } + async getByTag(tag: string): Promise> { return this.git.repos.getReleaseByTag({ owner: this.context.repo.owner,